From 12e75e4c8c70459aa62bebae9b297be86570662b Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 17 Jul 2023 11:38:25 -0700 Subject: [PATCH] build: remove libsysprof This library is going away now that we have -analyze and -profile libs. --- meson.build | 3 +- meson_options.txt | 9 - src/libsysprof-analyze/sysprof-elf.c | 2 +- src/libsysprof/binfile.c | 576 ----- src/libsysprof/binfile.h | 51 - src/libsysprof/demangle.cpp | 40 - src/libsysprof/demangle.h | 28 - src/libsysprof/elfparser.c | 903 -------- src/libsysprof/elfparser.h | 68 - src/libsysprof/mapped-ring-buffer-source.c | 121 -- src/libsysprof/mapped-ring-buffer-source.h | 47 - src/libsysprof/meson.build | 198 -- src/libsysprof/rax.c | 1927 ----------------- src/libsysprof/rax.h | 216 -- src/libsysprof/rax_malloc.h | 43 - .../sysprof-backport-autocleanups.h | 26 - src/libsysprof/sysprof-battery-source.c | 343 --- src/libsysprof/sysprof-battery-source.h | 35 - src/libsysprof/sysprof-callgraph-profile.c | 551 ----- src/libsysprof/sysprof-callgraph-profile.h | 51 - src/libsysprof/sysprof-capture-autocleanups.h | 41 - src/libsysprof/sysprof-capture-gobject.c | 92 - src/libsysprof/sysprof-capture-gobject.h | 58 - .../sysprof-capture-symbol-resolver.c | 137 -- .../sysprof-capture-symbol-resolver.h | 35 - src/libsysprof/sysprof-control-source.c | 313 --- src/libsysprof/sysprof-control-source.h | 35 - src/libsysprof/sysprof-diskstat-source.c | 472 ---- src/libsysprof/sysprof-diskstat-source.h | 35 - .../sysprof-elf-symbol-resolver-private.h | 33 - src/libsysprof/sysprof-elf-symbol-resolver.c | 686 ------ src/libsysprof/sysprof-elf-symbol-resolver.h | 53 - src/libsysprof/sysprof-flatpak.c | 158 -- src/libsysprof/sysprof-flatpak.h | 29 - src/libsysprof/sysprof-gjs-source.c | 68 - src/libsysprof/sysprof-gjs-source.h | 35 - src/libsysprof/sysprof-governor-source.c | 318 --- src/libsysprof/sysprof-governor-source.h | 40 - src/libsysprof/sysprof-helpers.c | 835 ------- src/libsysprof/sysprof-helpers.h | 121 -- src/libsysprof/sysprof-hostinfo-source.c | 496 ----- src/libsysprof/sysprof-hostinfo-source.h | 39 - .../sysprof-jitmap-symbol-resolver.c | 125 -- .../sysprof-jitmap-symbol-resolver.h | 38 - src/libsysprof/sysprof-kallsyms.c | 168 -- src/libsysprof/sysprof-kallsyms.h | 39 - .../sysprof-kernel-symbol-resolver.c | 156 -- .../sysprof-kernel-symbol-resolver.h | 38 - src/libsysprof/sysprof-kernel-symbol.c | 252 --- src/libsysprof/sysprof-kernel-symbol.h | 39 - src/libsysprof/sysprof-line-reader.c | 122 -- src/libsysprof/sysprof-line-reader.h | 40 - src/libsysprof/sysprof-local-profiler.c | 1217 ----------- src/libsysprof/sysprof-local-profiler.h | 51 - src/libsysprof/sysprof-map-lookaside.c | 143 -- src/libsysprof/sysprof-map-lookaside.h | 63 - src/libsysprof/sysprof-memory-source.c | 473 ---- src/libsysprof/sysprof-memory-source.h | 40 - src/libsysprof/sysprof-memprof-profile.c | 1173 ---------- src/libsysprof/sysprof-memprof-profile.h | 98 - src/libsysprof/sysprof-memprof-source.c | 87 - src/libsysprof/sysprof-memprof-source.h | 35 - src/libsysprof/sysprof-mountinfo.c | 352 --- src/libsysprof/sysprof-mountinfo.h | 41 - src/libsysprof/sysprof-netdev-source.c | 420 ---- src/libsysprof/sysprof-netdev-source.h | 35 - src/libsysprof/sysprof-path-resolver.c | 554 ----- src/libsysprof/sysprof-path-resolver.h | 41 - src/libsysprof/sysprof-perf-counter.c | 504 ----- src/libsysprof/sysprof-perf-counter.h | 151 -- src/libsysprof/sysprof-perf-source.c | 831 ------- src/libsysprof/sysprof-perf-source.h | 43 - src/libsysprof/sysprof-podman.c | 318 --- src/libsysprof/sysprof-podman.h | 37 - src/libsysprof/sysprof-polkit-private.h | 39 - src/libsysprof/sysprof-polkit.c | 177 -- src/libsysprof/sysprof-preload-source.c | 153 -- src/libsysprof/sysprof-preload-source.h | 32 - src/libsysprof/sysprof-private.h | 39 - src/libsysprof/sysprof-proc-source.c | 613 ------ src/libsysprof/sysprof-proc-source.h | 41 - src/libsysprof/sysprof-process-model-item.c | 255 --- src/libsysprof/sysprof-process-model-item.h | 54 - src/libsysprof/sysprof-process-model.c | 307 --- src/libsysprof/sysprof-process-model.h | 48 - src/libsysprof/sysprof-profile.c | 85 - src/libsysprof/sysprof-profile.h | 67 - src/libsysprof/sysprof-profiler.c | 312 --- src/libsysprof/sysprof-profiler.h | 191 -- src/libsysprof/sysprof-proxy-source.c | 753 ------- src/libsysprof/sysprof-proxy-source.h | 39 - src/libsysprof/sysprof-selection.c | 335 --- src/libsysprof/sysprof-selection.h | 72 - src/libsysprof/sysprof-source.c | 189 -- src/libsysprof/sysprof-source.h | 212 -- src/libsysprof/sysprof-spawnable.c | 340 --- src/libsysprof/sysprof-spawnable.h | 86 - src/libsysprof/sysprof-symbol-map.c | 593 ----- src/libsysprof/sysprof-symbol-map.h | 49 - .../sysprof-symbol-resolver-private.h | 31 - src/libsysprof/sysprof-symbol-resolver.c | 189 -- src/libsysprof/sysprof-symbol-resolver.h | 76 - src/libsysprof/sysprof-symbols-source.c | 168 -- src/libsysprof/sysprof-symbols-source.h | 40 - src/libsysprof/sysprof-tracefd-source.c | 302 --- src/libsysprof/sysprof-tracefd-source.h | 49 - src/libsysprof/sysprof.h | 67 - .../org.gnome.Sysprof.Agent.xml | 0 108 files changed, 2 insertions(+), 23092 deletions(-) delete mode 100644 src/libsysprof/binfile.c delete mode 100644 src/libsysprof/binfile.h delete mode 100644 src/libsysprof/demangle.cpp delete mode 100644 src/libsysprof/demangle.h delete mode 100644 src/libsysprof/elfparser.c delete mode 100644 src/libsysprof/elfparser.h delete mode 100644 src/libsysprof/mapped-ring-buffer-source.c delete mode 100644 src/libsysprof/mapped-ring-buffer-source.h delete mode 100644 src/libsysprof/meson.build delete mode 100644 src/libsysprof/rax.c delete mode 100644 src/libsysprof/rax.h delete mode 100644 src/libsysprof/rax_malloc.h delete mode 100644 src/libsysprof/sysprof-backport-autocleanups.h delete mode 100644 src/libsysprof/sysprof-battery-source.c delete mode 100644 src/libsysprof/sysprof-battery-source.h delete mode 100644 src/libsysprof/sysprof-callgraph-profile.c delete mode 100644 src/libsysprof/sysprof-callgraph-profile.h delete mode 100644 src/libsysprof/sysprof-capture-autocleanups.h delete mode 100644 src/libsysprof/sysprof-capture-gobject.c delete mode 100644 src/libsysprof/sysprof-capture-gobject.h delete mode 100644 src/libsysprof/sysprof-capture-symbol-resolver.c delete mode 100644 src/libsysprof/sysprof-capture-symbol-resolver.h delete mode 100644 src/libsysprof/sysprof-control-source.c delete mode 100644 src/libsysprof/sysprof-control-source.h delete mode 100644 src/libsysprof/sysprof-diskstat-source.c delete mode 100644 src/libsysprof/sysprof-diskstat-source.h delete mode 100644 src/libsysprof/sysprof-elf-symbol-resolver-private.h delete mode 100644 src/libsysprof/sysprof-elf-symbol-resolver.c delete mode 100644 src/libsysprof/sysprof-elf-symbol-resolver.h delete mode 100644 src/libsysprof/sysprof-flatpak.c delete mode 100644 src/libsysprof/sysprof-flatpak.h delete mode 100644 src/libsysprof/sysprof-gjs-source.c delete mode 100644 src/libsysprof/sysprof-gjs-source.h delete mode 100644 src/libsysprof/sysprof-governor-source.c delete mode 100644 src/libsysprof/sysprof-governor-source.h delete mode 100644 src/libsysprof/sysprof-helpers.c delete mode 100644 src/libsysprof/sysprof-helpers.h delete mode 100644 src/libsysprof/sysprof-hostinfo-source.c delete mode 100644 src/libsysprof/sysprof-hostinfo-source.h delete mode 100644 src/libsysprof/sysprof-jitmap-symbol-resolver.c delete mode 100644 src/libsysprof/sysprof-jitmap-symbol-resolver.h delete mode 100644 src/libsysprof/sysprof-kallsyms.c delete mode 100644 src/libsysprof/sysprof-kallsyms.h delete mode 100644 src/libsysprof/sysprof-kernel-symbol-resolver.c delete mode 100644 src/libsysprof/sysprof-kernel-symbol-resolver.h delete mode 100644 src/libsysprof/sysprof-kernel-symbol.c delete mode 100644 src/libsysprof/sysprof-kernel-symbol.h delete mode 100644 src/libsysprof/sysprof-line-reader.c delete mode 100644 src/libsysprof/sysprof-line-reader.h delete mode 100644 src/libsysprof/sysprof-local-profiler.c delete mode 100644 src/libsysprof/sysprof-local-profiler.h delete mode 100644 src/libsysprof/sysprof-map-lookaside.c delete mode 100644 src/libsysprof/sysprof-map-lookaside.h delete mode 100644 src/libsysprof/sysprof-memory-source.c delete mode 100644 src/libsysprof/sysprof-memory-source.h delete mode 100644 src/libsysprof/sysprof-memprof-profile.c delete mode 100644 src/libsysprof/sysprof-memprof-profile.h delete mode 100644 src/libsysprof/sysprof-memprof-source.c delete mode 100644 src/libsysprof/sysprof-memprof-source.h delete mode 100644 src/libsysprof/sysprof-mountinfo.c delete mode 100644 src/libsysprof/sysprof-mountinfo.h delete mode 100644 src/libsysprof/sysprof-netdev-source.c delete mode 100644 src/libsysprof/sysprof-netdev-source.h delete mode 100644 src/libsysprof/sysprof-path-resolver.c delete mode 100644 src/libsysprof/sysprof-path-resolver.h delete mode 100644 src/libsysprof/sysprof-perf-counter.c delete mode 100644 src/libsysprof/sysprof-perf-counter.h delete mode 100644 src/libsysprof/sysprof-perf-source.c delete mode 100644 src/libsysprof/sysprof-perf-source.h delete mode 100644 src/libsysprof/sysprof-podman.c delete mode 100644 src/libsysprof/sysprof-podman.h delete mode 100644 src/libsysprof/sysprof-polkit-private.h delete mode 100644 src/libsysprof/sysprof-polkit.c delete mode 100644 src/libsysprof/sysprof-preload-source.c delete mode 100644 src/libsysprof/sysprof-preload-source.h delete mode 100644 src/libsysprof/sysprof-private.h delete mode 100644 src/libsysprof/sysprof-proc-source.c delete mode 100644 src/libsysprof/sysprof-proc-source.h delete mode 100644 src/libsysprof/sysprof-process-model-item.c delete mode 100644 src/libsysprof/sysprof-process-model-item.h delete mode 100644 src/libsysprof/sysprof-process-model.c delete mode 100644 src/libsysprof/sysprof-process-model.h delete mode 100644 src/libsysprof/sysprof-profile.c delete mode 100644 src/libsysprof/sysprof-profile.h delete mode 100644 src/libsysprof/sysprof-profiler.c delete mode 100644 src/libsysprof/sysprof-profiler.h delete mode 100644 src/libsysprof/sysprof-proxy-source.c delete mode 100644 src/libsysprof/sysprof-proxy-source.h delete mode 100644 src/libsysprof/sysprof-selection.c delete mode 100644 src/libsysprof/sysprof-selection.h delete mode 100644 src/libsysprof/sysprof-source.c delete mode 100644 src/libsysprof/sysprof-source.h delete mode 100644 src/libsysprof/sysprof-spawnable.c delete mode 100644 src/libsysprof/sysprof-spawnable.h delete mode 100644 src/libsysprof/sysprof-symbol-map.c delete mode 100644 src/libsysprof/sysprof-symbol-map.h delete mode 100644 src/libsysprof/sysprof-symbol-resolver-private.h delete mode 100644 src/libsysprof/sysprof-symbol-resolver.c delete mode 100644 src/libsysprof/sysprof-symbol-resolver.h delete mode 100644 src/libsysprof/sysprof-symbols-source.c delete mode 100644 src/libsysprof/sysprof-symbols-source.h delete mode 100644 src/libsysprof/sysprof-tracefd-source.c delete mode 100644 src/libsysprof/sysprof-tracefd-source.h delete mode 100644 src/libsysprof/sysprof.h rename src/{ => sysprof-agent}/org.gnome.Sysprof.Agent.xml (100%) diff --git a/meson.build b/meson.build index 5aecd7a8..f56df63b 100644 --- a/meson.build +++ b/meson.build @@ -259,8 +259,7 @@ int main(void) { config_h.set10('HAVE_STDATOMIC_H', true) endif -needs_service_access = get_option('libsysprof') or get_option('agent') -install_service_files = needs_service_access or get_option('sysprofd') == 'bundled' +install_service_files = get_option('sysprofd') == 'bundled' if need_glib subdir('contrib') diff --git a/meson_options.txt b/meson_options.txt index 100ec6b8..4df1a16c 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -8,9 +8,6 @@ option('development', type: 'boolean', value: 'false') # server scenarios. option('gtk', type: 'boolean') -# Disable libsysprof/ui (in situations you only want sysprof-capture) -option('libsysprof', type: 'boolean') - # Allow disabling the installation of libsysprof-capture*.a option('install-static', type: 'boolean') @@ -29,12 +26,6 @@ option('systemdunitdir', type: 'string', description: 'Directory for systemd service files' ) -# An optional location to specify where to locate debug information. This -# is useful for distributions to set based on their debuginfo setup. -option('debugdir', type: 'string', - description: 'Look for global separate debug info in this path' -) - # If Yelp documentation should be installed option('help', type: 'boolean') diff --git a/src/libsysprof-analyze/sysprof-elf.c b/src/libsysprof-analyze/sysprof-elf.c index 98b2b9b6..2542346a 100644 --- a/src/libsysprof-analyze/sysprof-elf.c +++ b/src/libsysprof-analyze/sysprof-elf.c @@ -20,7 +20,7 @@ #include "config.h" -#include "../libsysprof/elfparser.h" +#include "elfparser.h" #include "sysprof-elf-private.h" diff --git a/src/libsysprof/binfile.c b/src/libsysprof/binfile.c deleted file mode 100644 index a68598ff..00000000 --- a/src/libsysprof/binfile.c +++ /dev/null @@ -1,576 +0,0 @@ -/* MemProf -- memory profiler and leak detector - * Copyright 1999, 2000, 2001, Red Hat, Inc. - * Copyright 2002, Kristian Rietveld - * - * Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, 2005, 2006, 2007, Soeren Sandmann - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* Most interesting code in this file is lifted from bfdutils.c - * and process.c from Memprof, - */ -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "binfile.h" -#include "elfparser.h" - -struct bin_file_t -{ - int ref_count; - - GList * elf_files; - - char * filename; - - char * undefined_name; - - gulong text_offset; - - gboolean inode_check; - ino_t inode; -}; - -static ino_t -read_inode (const char *filename) -{ - struct stat statbuf; - - if (strcmp (filename, "[vdso]") == 0) - return (ino_t)0; - - if (stat (filename, &statbuf) < 0) - return (ino_t)-1; - - return statbuf.st_ino; -} - -static gboolean -already_warned (const char *name) -{ - static GPtrArray *warnings; - guint i; - - if (!warnings) - warnings = g_ptr_array_new (); - - for (i = 0; i < warnings->len; ++i) - { - if (strcmp (warnings->pdata[i], name) == 0) - return TRUE; - } - - g_ptr_array_add (warnings, g_strdup (name)); - - return FALSE; -} - -static ElfParser * -get_build_id_file (ElfParser *elf, - const char * const *debug_dirs) -{ - const char *build_id; - GList *tries = NULL, *list; - char *init, *rest; - ElfParser *result = NULL; - char *tmp; - - build_id = elf_parser_get_build_id (elf); - - if (!build_id) - return NULL; - - if (strlen (build_id) < 4) - return NULL; - - init = g_strndup (build_id, 2); - rest = g_strdup_printf ("%s%s", build_id + 2, ".debug"); - - if (debug_dirs) - { - for (guint i = 0; debug_dirs[i]; i++) - { - tmp = g_build_filename (debug_dirs[i], ".build-id", init, rest, NULL); - tries = g_list_append (tries, tmp); - } - } - - for (list = tries; list != NULL; list = list->next) - { - char *name = list->data; - ElfParser *parser = elf_parser_new (name, NULL); - - if (parser) - { - const char *file_id = elf_parser_get_build_id (parser); - - if (file_id && strcmp (build_id, file_id) == 0) - { - result = parser; - break; - } - - elf_parser_free (parser); - } - } - - g_list_foreach (tries, (GFunc)g_free, NULL); - g_list_free (tries); - - g_free (init); - g_free (rest); - - return result; -} - -static ElfParser * -get_debuglink_file (ElfParser *elf, - const char *filename, - char **new_name, - const gchar * const *debug_dirs) -{ - const char *basename; - guint32 crc32; - ElfParser *result = NULL; - const char *build_id; - char *dir; - const char *files; - const char *prefix = ""; - - if (!elf) - return NULL; - - basename = elf_parser_get_debug_link (elf, &crc32); - - build_id = elf_parser_get_build_id (elf); - -#if 0 - g_print (" debug link for %s is %s\n", filename, basename); -#endif - - if (!basename) - return NULL; - - dir = g_path_get_dirname (filename); - - /* Flatpak paths have the form of ".../files/bin" or ".../files/lib/x86_64-linux-gnu". */ - files = g_strrstr (dir, "/files/"); - if (files) - prefix = files + sizeof ("/files/") - 1; - - for (guint i = 0; debug_dirs[i]; i++) - { - /* Most files from Flatpak will be from .Platform, which usually has a prefix like "lib/x86_64-linux-gnu" - * but in the debug dir the files are under "usr/lib/x86_64-linux-gnu", so try with "usr" first. */ - g_autofree char *name = g_build_filename (debug_dirs[i], "usr", prefix, basename, NULL); - ElfParser *parser = elf_parser_new (name, NULL); - guint32 file_crc; - const char *file_build_id; - - if (!parser) - { - /* Files from Flatpak com.example.App.Debug have prefixes like "bin" or "lib", - * and they don't need the "usr" for the debug dir, so try without "usr". */ - g_free (name); - name = g_build_filename (debug_dirs[i], prefix, basename, NULL); - parser = elf_parser_new (name, NULL); - } - - if (parser) - { - /* If both files have build ids, and they don't match, - * there is no point computing a CRC32 that we know - * will fail - */ - file_build_id = elf_parser_get_build_id (parser); - if (build_id && file_build_id && strcmp (build_id, file_build_id) != 0) - goto skip; - - file_crc = elf_parser_get_crc32 (parser); - - if (file_crc == crc32) - { - result = parser; - *new_name = g_steal_pointer (&name); - break; - } - else - { - if (!already_warned (name)) - { - g_print ("warning: %s has wrong crc %x, %s has crc %x)\n", - name, file_crc, filename, crc32); - } - } - - skip: - elf_parser_free (parser); - } - } - - g_free (dir); - - return result; -} - -static GList * -get_debug_binaries (GList *files, - ElfParser *elf, - const char *filename, - const gchar * const *debug_dirs) -{ - ElfParser *build_id_file; - GHashTable *seen_names; - GList *free_us = NULL; - - build_id_file = get_build_id_file (elf, debug_dirs); - - if (build_id_file) - return g_list_prepend (files, build_id_file); - - /* .gnu_debuglink is actually a chain of debuglinks, and - * there have been real-world cases where following it was - * necessary to get useful debug information. - */ - seen_names = g_hash_table_new (g_str_hash, g_str_equal); - - while (elf) - { - char *debug_name; - - if (g_hash_table_lookup (seen_names, filename)) - break; - - g_hash_table_insert (seen_names, (char *)filename, (char *)filename); - - elf = get_debuglink_file (elf, filename, &debug_name, debug_dirs); - - if (elf) - { - files = g_list_prepend (files, elf); - free_us = g_list_prepend (free_us, debug_name); - filename = debug_name; - } - } - - g_list_foreach (free_us, (GFunc)g_free, NULL); - g_list_free (free_us); - - g_hash_table_destroy (seen_names); - - return files; -} - -#ifdef __linux__ -G_GNUC_PRINTF (1, 2) -static char ** -get_lines (const char *format, - ...) -{ - va_list args; - char *filename; - char **result = NULL; - char *contents; - - va_start (args, format); - filename = g_strdup_vprintf (format, args); - va_end (args); - - if (g_file_get_contents (filename, &contents, NULL, NULL)) - { - result = g_strsplit (contents, "\n", -1); - - g_free (contents); - } - - g_free (filename); - - return result; -} -#endif - -static const uint8_t * -get_vdso_bytes (size_t *length) -{ -#ifdef __linux__ - static const uint8_t *bytes = NULL; - static size_t n_bytes = 0; - static gboolean has_data; - - if (!has_data) - { - char **lines = get_lines ("/proc/%d/maps", getpid()); - int i; - - for (i = 0; lines[i] != NULL; ++i) - { - char file[256]; - gulong start; - gulong end; - int count = sscanf ( - lines[i], "%lx-%lx %*15s %*x %*x:%*x %*u %255s", - &start, &end, file); - - if (count == 3 && strcmp (file, "[vdso]") == 0) - { - n_bytes = end - start; - - /* Dup the memory here so that valgrind will only - * report one 1 byte invalid read instead of - * a ton when the elf parser scans the vdso - * - * The reason we get a spurious invalid read from - * valgrind is that we are getting the address directly - * from /proc/maps, and valgrind knows that its mmap() - * wrapper never returned that address. But since it - * is a legal mapping, it is legal to read it. - */ - bytes = g_memdup2 ((uint8_t *)start, n_bytes); - - has_data = TRUE; - } - } - } - - if (length) - *length = n_bytes; - - return bytes; -#else - if (length) - *length = 0; - return NULL; -#endif -} - -bin_file_t * -bin_file_new (const char *filename, - const gchar * const *debug_dirs) -{ - const gchar *real_filename = filename; - ElfParser *elf = NULL; - bin_file_t *bf; - - bf = g_new0 (bin_file_t, 1); - - bf->inode_check = FALSE; - bf->filename = g_strdup (filename); - bf->undefined_name = g_strdup_printf ("In file %s", real_filename); - bf->ref_count = 1; - bf->elf_files = NULL; - - if (strcmp (filename, "[vdso]") == 0) - { - const guint8 *vdso_bytes; - gsize length; - - vdso_bytes = get_vdso_bytes (&length); - - if (vdso_bytes) - elf = elf_parser_new_from_data (vdso_bytes, length); - } - else - { - elf = elf_parser_new (filename, NULL); - } - - if (elf) - { - /* We need the text offset of the actual binary, not the - * (potential) debug binaries - */ - bf->text_offset = elf_parser_get_text_offset (elf); - - bf->elf_files = get_debug_binaries (bf->elf_files, elf, filename, debug_dirs); - bf->elf_files = g_list_append (bf->elf_files, elf); - - bf->inode = read_inode (filename); - } - - return bf; -} - -void -bin_file_free (bin_file_t *bin_file) -{ - if (--bin_file->ref_count == 0) - { - g_list_foreach (bin_file->elf_files, (GFunc)elf_parser_free, NULL); - g_list_free (bin_file->elf_files); - - g_free (bin_file->filename); - g_free (bin_file->undefined_name); - g_free (bin_file); - } -} - -const bin_symbol_t * -bin_file_lookup_symbol (bin_file_t *bin_file, - gulong address) -{ - GList *list; - -#if 0 - g_print ("-=-=-=- \n"); - - g_print ("bin file lookup lookup %d\n", address); -#endif - - address -= bin_file->text_offset; - -#if 0 - g_print ("lookup %lx in %s\n", address, bin_file->filename); -#endif - - for (list = bin_file->elf_files; list != NULL; list = list->next) - { - ElfParser *elf = list->data; - const ElfSym *sym = elf_parser_lookup_symbol (elf, address); - - if (sym) - { -#if 0 - g_print ("found %lx => %s\n", address, - bin_symbol_get_name (bin_file, (const bin_symbol_t *)sym)); -#endif - return (const bin_symbol_t *)sym; - } - } - -#if 0 - g_print ("%lx undefined in %s (textoffset %lx)\n", - address + bin_file->text_offset, - bin_file->filename, - bin_file->text_offset); -#endif - - return (const bin_symbol_t *)bin_file->undefined_name; -} - -gboolean -bin_file_check_inode (bin_file_t *bin_file, - ino_t inode) -{ - if (bin_file->inode == inode) - return TRUE; - - if (!bin_file->elf_files) - return FALSE; - - if (!bin_file->inode_check) - { - g_print ("warning: Inode mismatch for %s (disk: %"G_GUINT64_FORMAT", memory: %"G_GUINT64_FORMAT")\n", - bin_file->filename, (guint64)bin_file->inode, (guint64)inode); - - bin_file->inode_check = TRUE; - } - - return FALSE; -} - -static const ElfSym * -get_elf_sym (bin_file_t *file, - const bin_symbol_t *symbol, - ElfParser **elf_ret) -{ - GList *list; - - for (list = file->elf_files; list != NULL; list = list->next) - { - const ElfSym *sym = (const ElfSym *)symbol; - ElfParser *elf = list->data; - - if (elf_parser_owns_symbol (elf, sym)) - { - *elf_ret = elf; - return sym; - } - } - - g_critical ("Internal error: unrecognized symbol pointer"); - - *elf_ret = NULL; - return NULL; -} - -const char * -bin_symbol_get_name (bin_file_t *file, - const bin_symbol_t *symbol) -{ - if (file->undefined_name == (char *)symbol) - { - return file->undefined_name; - } - else - { - ElfParser *elf; - const ElfSym *sym; - - sym = get_elf_sym (file, symbol, &elf); - - return elf_parser_get_sym_name (elf, sym); - } -} - -gulong -bin_symbol_get_address (bin_file_t *file, - const bin_symbol_t *symbol) -{ - if (file->undefined_name == (char *)symbol) - { - return 0x0; - } - else - { - ElfParser *elf; - const ElfSym *sym; - - sym = get_elf_sym (file, symbol, &elf); - - return elf_parser_get_sym_address (elf, sym); - } -} - -void -bin_symbol_get_address_range (bin_file_t *file, - const bin_symbol_t *symbol, - gulong *begin, - gulong *end) -{ - if (file->undefined_name == (char *)symbol) - { - *begin = 0; - *end = 0; - } - else - { - ElfParser *elf; - const ElfSym *sym; - - sym = get_elf_sym (file, symbol, &elf); - elf_parser_get_sym_address_range (elf, sym, begin, end); - } -} diff --git a/src/libsysprof/binfile.h b/src/libsysprof/binfile.h deleted file mode 100644 index 2feb1188..00000000 --- a/src/libsysprof/binfile.h +++ /dev/null @@ -1,51 +0,0 @@ -/* MemProf -- memory profiler and leak detector - * Copyright 1999, 2000, 2001, Red Hat, Inc. - * Copyright 2002, Kristian Rietveld - * - * Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, Red Hat, Inc. - * Copyright 2004, 2005, Soeren Sandmann - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef BIN_FILE_H -#define BIN_FILE_H - -#include -#include - -typedef struct bin_file_t bin_file_t; -typedef struct bin_symbol_t bin_symbol_t; - -/* Binary File */ - -bin_file_t *bin_file_new (const char *filename, - const gchar * const *debug_dirs); -void bin_file_free (bin_file_t *bin_file); -const bin_symbol_t *bin_file_lookup_symbol (bin_file_t *bin_file, - gulong address); -gboolean bin_file_check_inode (bin_file_t *bin_file, - ino_t inode); -const char *bin_symbol_get_name (bin_file_t *bin_file, - const bin_symbol_t *symbol); -gulong bin_symbol_get_address (bin_file_t *bin_file, - const bin_symbol_t *symbol); -void bin_symbol_get_address_range (bin_file_t *bin_file, - const bin_symbol_t *symbol, - gulong *begin, - gulong *end); - -#endif diff --git a/src/libsysprof/demangle.cpp b/src/libsysprof/demangle.cpp deleted file mode 100644 index 1f941cd2..00000000 --- a/src/libsysprof/demangle.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* demangle.cpp - * - * Copyright (C) 2016 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 . - */ - -#include -#include - -#include "demangle.h" - -gchar * -sysprof_cplus_demangle (const gchar *name) -{ - char *real_name; - gchar *ret; - int status; - - real_name = abi::__cxa_demangle (name, 0, 0, &status); - - if (real_name == NULL) - return NULL; - - ret = g_strdup (real_name); - free (real_name); - - return ret; -} diff --git a/src/libsysprof/demangle.h b/src/libsysprof/demangle.h deleted file mode 100644 index 26d28fdc..00000000 --- a/src/libsysprof/demangle.h +++ /dev/null @@ -1,28 +0,0 @@ -/* demangle.h - * - * Copyright 2016-2019 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 . - */ - -#pragma once - -#include - -G_BEGIN_DECLS - -G_GNUC_INTERNAL -gchar *sysprof_cplus_demangle (const gchar *name); - -G_END_DECLS diff --git a/src/libsysprof/elfparser.c b/src/libsysprof/elfparser.c deleted file mode 100644 index 2c6b638b..00000000 --- a/src/libsysprof/elfparser.c +++ /dev/null @@ -1,903 +0,0 @@ -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2006, 2007, Soeren Sandmann - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#ifdef __APPLE__ -# include -#else -# include -#endif -#include - -#include "demangle.h" -#include "elfparser.h" - -typedef struct Section Section; - -struct ElfSym -{ - gulong table; - gulong offset; - gulong address; -}; - -struct Section -{ - const gchar * name; - gsize offset; - gsize size; - gboolean allocated; - gulong load_address; - guint type; -}; - -struct ElfParser -{ - gboolean is_64; - const guchar * data; - gsize length; - - guint n_sections; - Section ** sections; - - guint n_symbols; - ElfSym * symbols; - gsize sym_strings; - - GMappedFile * file; - - char * filename; - - gboolean checked_build_id; - char * build_id; - - const Section * text_section; -}; - -/* FIXME: All of these should in principle do endian swapping, - * but sysprof never has to deal with binaries of a different - * endianness than sysprof itself - */ -#define GET_FIELD(parser, offset, struct_name, idx, field_name) \ - (((parser))->is_64? \ - ((Elf64_ ## struct_name *)(gpointer)(((parser)->data + offset)) + (idx))->field_name : \ - ((Elf32_ ## struct_name *)(gpointer)(((parser)->data + offset)) + (idx))->field_name) - -#define GET_UINT32(parser, offset) \ - *((uint32_t *)(gpointer)(parser->data + offset)) \ - -#define GET_SIZE(parser, struct_name) \ - (((parser)->is_64? \ - sizeof (Elf64_ ## struct_name) : \ - sizeof (Elf32_ ## struct_name))) - -#define MAKE_ELF_UINT_ACCESSOR(field_name) \ - static uint64_t field_name (ElfParser *parser) \ - { \ - return GET_FIELD (parser, 0, Ehdr, 0, field_name); \ - } - -MAKE_ELF_UINT_ACCESSOR (e_shoff) -MAKE_ELF_UINT_ACCESSOR (e_shnum) -MAKE_ELF_UINT_ACCESSOR (e_shstrndx) - -#define MAKE_SECTION_HEADER_ACCESSOR(field_name) \ - static uint64_t field_name (ElfParser *parser, int nth_section) \ - { \ - gsize offset = e_shoff (parser); \ - \ - return GET_FIELD (parser, offset, Shdr, nth_section, field_name); \ - } - -MAKE_SECTION_HEADER_ACCESSOR (sh_name); -MAKE_SECTION_HEADER_ACCESSOR (sh_type); -MAKE_SECTION_HEADER_ACCESSOR (sh_flags); -MAKE_SECTION_HEADER_ACCESSOR (sh_addr); -MAKE_SECTION_HEADER_ACCESSOR (sh_offset); -MAKE_SECTION_HEADER_ACCESSOR (sh_size); - -#define MAKE_SYMBOL_ACCESSOR(field_name) \ - static uint64_t field_name (ElfParser *parser, gulong offset, gulong nth) \ - { \ - return GET_FIELD (parser, offset, Sym, nth, field_name); \ - } - -MAKE_SYMBOL_ACCESSOR(st_name); -MAKE_SYMBOL_ACCESSOR(st_info); -MAKE_SYMBOL_ACCESSOR(st_value); -MAKE_SYMBOL_ACCESSOR(st_size); -MAKE_SYMBOL_ACCESSOR(st_shndx); - -static gboolean -in_container (void) -{ - static gboolean _in_container; - static gboolean initialized; - - if (!initialized) - { - /* Flatpak has /.flatpak-info - * Podman has /run/.containerenv - * - * Both have access to host files via /var/run/host. - */ - _in_container = g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS) || - g_file_test ("/run/.containerenv", G_FILE_TEST_EXISTS); - - initialized = TRUE; - } - - return _in_container; -} - -static void -section_free (Section *section) -{ - g_free (section); -} - -static const Section * -find_section (ElfParser *parser, - const char *name, - guint type) -{ - guint i; - - for (i = 0; i < parser->n_sections; ++i) - { - Section *section = parser->sections[i]; - - if (strcmp (section->name, name) == 0 && section->type == type) - return section; - } - - return NULL; -} - -static gboolean -parse_elf_signature (const guchar *data, - gsize length, - gboolean *is_64, - gboolean *is_be) -{ - /* FIXME: this function should be able to return an error */ - if (length < EI_NIDENT) - { - /* FIXME set error */ - return FALSE; - } - - if (data[EI_CLASS] != ELFCLASS32 && - data[EI_CLASS] != ELFCLASS64) - { - /* FIXME set error */ - return FALSE; - } - - if (data[EI_DATA] != ELFDATA2LSB && - data[EI_DATA] != ELFDATA2MSB) - { - /* FIXME set error */ - return FALSE; - } - - if (is_64) - *is_64 = (data[EI_CLASS] == ELFCLASS64); - - if (is_be) - *is_be = (data[EI_DATA] == ELFDATA2MSB); - - return TRUE; -} - -ElfParser * -elf_parser_new_from_data (const guchar *data, - gsize length) -{ - ElfParser *parser; - gboolean is_64, is_big_endian; - int section_names_idx; - const guchar *section_names; - G_GNUC_UNUSED gsize section_headers; - guint i; - - if (!parse_elf_signature (data, length, &is_64, &is_big_endian)) - { - /* FIXME: set error */ - return NULL; - } - - parser = g_new0 (ElfParser, 1); - - parser->is_64 = is_64; - parser->data = data; - parser->length = length; - -#if 0 - g_print (" new parser : %p\n", parser); -#endif - - /* Read ELF header */ - - parser->n_sections = e_shnum (parser); - section_names_idx = e_shstrndx (parser); - section_headers = e_shoff (parser); - - /* Read section headers */ - parser->sections = g_new0 (Section *, parser->n_sections); - - section_names = parser->data + sh_offset (parser, section_names_idx); - - for (i = 0; i < parser->n_sections; ++i) - { - Section *section = g_new (Section, 1); - - section->name = (char *)(section_names + sh_name (parser, i)); - section->size = sh_size (parser, i); - section->offset = sh_offset (parser, i); - section->allocated = !!(sh_flags (parser, i) & SHF_ALLOC); - - if (section->allocated) - section->load_address = sh_addr (parser, i); - else - section->load_address = 0; - - section->type = sh_type (parser, i); - - parser->sections[i] = section; - } - - /* Cache the text section */ - parser->text_section = find_section (parser, ".text", SHT_PROGBITS); - if (!parser->text_section) - parser->text_section = find_section (parser, ".text", SHT_NOBITS); - - parser->filename = NULL; - parser->build_id = NULL; - - return parser; -} - -static GMappedFile * -open_mapped_file (const char *filename, - GError **error) -{ - GMappedFile *file = NULL; - char *alternate = NULL; - - if (in_container () && !g_str_has_prefix (filename, g_get_home_dir ())) - { - alternate = g_build_filename ("/var/run/host", filename, NULL); - file = g_mapped_file_new (alternate, FALSE, NULL); - g_free (alternate); - } - - /* Flatpaks with filesystem=host don't have Silverblue's /sysroot in /var/run/host, - * yet they have it in /, which means the original path might work. - */ - if (!file) - file = g_mapped_file_new (filename, FALSE, error); - - return file; -} - -ElfParser * -elf_parser_new_from_mmap (GMappedFile *file, - GError **error) -{ - const guchar *data; - gsize length; - ElfParser *parser; - - if (file == NULL) - return NULL; - - data = (guchar *)g_mapped_file_get_contents (file); - length = g_mapped_file_get_length (file); - parser = elf_parser_new_from_data (data, length); - - if (!parser) - { - g_set_error (error, - G_FILE_ERROR, - G_FILE_ERROR_FAILED, - "Failed to load ELF from mmap region"); - g_mapped_file_unref (file); - return NULL; - } - - parser->filename = NULL; - parser->file = file; - - return parser; -} - -ElfParser * -elf_parser_new (const char *filename, - GError **error) -{ - GMappedFile *file; - const guchar *data; - gsize length; - ElfParser *parser; - - if (!(file = open_mapped_file (filename, error))) - return NULL; - -#if 0 - g_print ("elf parser new : %s\n", filename); -#endif - - data = (guchar *)g_mapped_file_get_contents (file); - length = g_mapped_file_get_length (file); - -#if 0 - g_print ("data %p: for %s\n", data, filename); -#endif - - parser = elf_parser_new_from_data (data, length); - -#if 0 - g_print ("Parser for %s: %p\n", filename, parser); -#endif - - if (!parser) - { - g_set_error (error, - G_FILE_ERROR, - G_FILE_ERROR_FAILED, - "Failed to load ELF from file %s", - filename); - g_mapped_file_unref (file); - return NULL; - } - - parser->filename = g_strdup (filename); - - parser->file = file; - -#if 0 - g_print ("Elf file: %s (debug: %s)\n", - filename, elf_parser_get_debug_link (parser, NULL)); - - if (!parser->symbols) - g_print ("at this point %s has no symbols\n", filename); -#endif - - return parser; -} - -guint32 -elf_parser_get_crc32 (ElfParser *parser) -{ - static const unsigned long crc32_table[256] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, - 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, - 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, - 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, - 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, - 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, - 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, - 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, - 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, - 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, - 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, - 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d - }; - const guchar *data; - gsize length; - gulong crc; - gsize i; - - data = parser->data; - length = parser->length; - - crc = 0xffffffff; - - madvise ((char *)data, length, MADV_SEQUENTIAL); - - for (i = 0; i < length; ++i) - crc = crc32_table[(crc ^ data[i]) & 0xff] ^ (crc >> 8); - - /* We just read the entire file into memory, but we only really - * need the symbol table, so swap the whole thing out. - * - * We could be more exact here, but it's only a few minor - * pagefaults. - */ - if (parser->file) - madvise ((char *)data, length, MADV_DONTNEED); - - return ~crc & 0xffffffff; -} - -void -elf_parser_free (ElfParser *parser) -{ - guint i; - - for (i = 0; i < parser->n_sections; ++i) - section_free (parser->sections[i]); - g_free (parser->sections); - - if (parser->file) - g_mapped_file_unref (parser->file); - - g_free (parser->symbols); - - if (parser->filename) - g_free (parser->filename); - - if (parser->build_id) - g_free (parser->build_id); - - g_free (parser); -} - -gchar * -elf_demangle (const char *name) -{ - gchar *demangled = sysprof_cplus_demangle (name); - - if (demangled) - return demangled; - else - return g_strdup (name); -} - -/* - * Looking up symbols - */ -static int -compare_sym (const void *a, const void *b) -{ - const ElfSym *sym_a = a; - const ElfSym *sym_b = b; - - if (sym_a->address < sym_b->address) - return -1; - else if (sym_a->address == sym_b->address) - return 0; - else - return 1; -} - -#if 0 -static void -dump_symbols (ElfParser *parser, ElfSym *syms, guint n_syms) -{ - int i; - - for (i = 0; i < n_syms; ++i) - { - ElfSym *s = &(syms[i]); - - g_print (" %s: %lx\n", elf_parser_get_sym_name (parser, s), s->address); - } -} -#endif - -static void -read_table (ElfParser *parser, - const Section *sym_table, - const Section *str_table) -{ - int sym_size = GET_SIZE (parser, Sym); - guint i, n_symbols; - -#if 0 - g_print ("elf: Reading table for %s\n", parser->filename? parser->filename : ""); -#endif - - parser->n_symbols = sym_table->size / sym_size; - parser->symbols = g_new (ElfSym, parser->n_symbols); - -#if 0 - g_print ("sym table offset: %d\n", sym_table->offset); -#endif - - n_symbols = 0; -#if 0 - g_print ("n syms: %d\n", parser->n_symbols); -#endif - for (i = 0; i < parser->n_symbols; ++i) - { - guint info; - gulong addr; - gulong shndx; - - info = st_info (parser, sym_table->offset, i); - addr = st_value (parser, sym_table->offset, i); - shndx = st_shndx (parser, sym_table->offset, i); - -#if 0 - g_print ("read symbol: %s (section: %d)\n", get_string_indirct (parser->parser, - parser->sym_format, "st_name", - str_table->offset), - shndx); -#endif - - if (addr != 0 && - shndx < parser->n_sections && - parser->sections[shndx] == parser->text_section && - (info & 0xf) == STT_FUNC && - ((info >> 4) == STB_GLOBAL || - (info >> 4) == STB_LOCAL || - (info >> 4) == STB_WEAK)) - { - parser->symbols[n_symbols].address = addr; - parser->symbols[n_symbols].table = sym_table->offset; - parser->symbols[n_symbols].offset = i; - - n_symbols++; - -#if 0 - g_print (" symbol: %s: %lx\n", - get_string_indirect (parser->parser, - parser->sym_format, "st_name", - str_table->offset), - addr - parser->text_section->load_address); - g_print (" sym %d in %p (info: %d:%d) (func:global %d:%d)\n", - addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL); -#endif - } - else if (addr != 0) - { -#if 0 - g_print (" rejecting %d in %p (info: %d:%d) (func:global %d:%d)\n", - addr, parser, info & 0xf, info >> 4, STT_FUNC, STB_GLOBAL); -#endif - } - } - - parser->sym_strings = str_table->offset; - parser->n_symbols = n_symbols; - - /* Allocate space for at least one symbol, so that parser->symbols will be - * non-NULL. If it ends up being NULL, we will be parsing the file over and - * over. - */ - parser->symbols = g_renew (ElfSym, parser->symbols, parser->n_symbols + 1); - - qsort (parser->symbols, parser->n_symbols, sizeof (ElfSym), compare_sym); -} - -static void -read_symbols (ElfParser *parser) -{ - const Section *symtab = find_section (parser, ".symtab", SHT_SYMTAB); - const Section *strtab = find_section (parser, ".strtab", SHT_STRTAB); - const Section *dynsym = find_section (parser, ".dynsym", SHT_DYNSYM); - const Section *dynstr = find_section (parser, ".dynstr", SHT_STRTAB); - - if (symtab && strtab) - { -#if 0 - g_print ("reading symbol table of %s\n", parser->filename); -#endif - read_table (parser, symtab, strtab); - } - else if (dynsym && dynstr) - { -#if 0 - g_print ("reading dynamic symbol table of %s\n", parser->filename); -#endif - read_table (parser, dynsym, dynstr); - } - else - { - /* To make sure parser->symbols is non-NULL */ - parser->n_symbols = 0; - parser->symbols = g_new (ElfSym, 1); - } -} - -static ElfSym * -do_lookup (ElfSym *symbols, - gulong address, - int first, - int last) -{ - if (address >= symbols[last].address) - { - return &(symbols[last]); - } - else if (last - first < 3) - { - while (last >= first) - { - if (address >= symbols[last].address) - return &(symbols[last]); - - last--; - } - - return NULL; - } - else - { - int mid = (first + last) / 2; - - if (symbols[mid].address > address) - return do_lookup (symbols, address, first, mid); - else - return do_lookup (symbols, address, mid, last); - } -} - -/* Address should be given in 'offset into text segment' */ -const ElfSym * -elf_parser_lookup_symbol (ElfParser *parser, - gulong address) -{ - const ElfSym *result; - - if (!parser->symbols) - { -#if 0 - g_print ("reading symbols at %p\n", parser); -#endif - read_symbols (parser); - } - - if (parser->n_symbols == 0) - return NULL; - - if (!parser->text_section) - return NULL; - - address += parser->text_section->load_address; - -#if 0 - g_print ("elf: the address we are looking up is %p\n", address); -#endif - - result = do_lookup (parser->symbols, address, 0, parser->n_symbols - 1); - -#if 0 - if (result) - { - g_print (" elf: found %s at %lx\n", elf_parser_get_sym_name (parser, result), result->address); - } - else - { - g_print ("elf: not found\n"); - } -#endif - - if (result) - { - gulong size = st_size (parser, result->table, result->offset); - - if (size > 0 && result->address + size <= address) - { -#if 0 - g_print (" elf: ends at %lx, so rejecting\n", - result->address + size); -#endif - result = NULL; - } - } - - if (result) - { - /* Reject the symbols if the address is outside the text section */ - if (address > parser->text_section->load_address + parser->text_section->size) - result = NULL; - } - - return result; -} - -gulong -elf_parser_get_text_offset (ElfParser *parser) -{ - g_return_val_if_fail (parser != NULL, (gulong)-1); - - if (!parser->text_section) - return (gulong)-1; - - return parser->text_section->offset; -} - -static gchar * -make_hex_string (const guchar *data, int n_bytes) -{ - static const char hex_digits[] = { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; - GString *string = g_string_new (NULL); - int i; - - for (i = 0; i < n_bytes; ++i) - { - char c = data[i]; - - g_string_append_c (string, hex_digits[(c & 0xf0) >> 4]); - g_string_append_c (string, hex_digits[(c & 0x0f)]); - } - - return g_string_free (string, FALSE); -} - -const gchar * -elf_parser_get_build_id (ElfParser *parser) -{ - if (!parser->checked_build_id) - { - const Section *build_id = - find_section (parser, ".note.gnu.build-id", SHT_NOTE); - guint64 name_size; - guint64 desc_size; - guint64 type; - const char *name; - guint64 offset; - - parser->checked_build_id = TRUE; - - if (!build_id) - return NULL; - - offset = build_id->offset; - - name_size = GET_FIELD (parser, offset, Nhdr, 0, n_namesz); - desc_size = GET_FIELD (parser, offset, Nhdr, 0, n_descsz); - type = GET_FIELD (parser, offset, Nhdr, 0, n_type); - - offset += GET_SIZE (parser, Nhdr); - - name = (char *)(parser->data + offset); - - if (strncmp (name, ELF_NOTE_GNU, name_size) != 0 || type != NT_GNU_BUILD_ID) - return NULL; - - offset += strlen (name); - - offset = (offset + 3) & (~0x3); - - parser->build_id = make_hex_string (parser->data + offset, desc_size); - } - - return parser->build_id; -} - -const char * -elf_parser_get_debug_link (ElfParser *parser, - guint32 *crc32) -{ - guint64 offset; - const Section *debug_link = find_section (parser, ".gnu_debuglink", - SHT_PROGBITS); - const gchar *result; - - if (!debug_link) - return NULL; - - offset = debug_link->offset; - - result = (char *)(parser->data + offset); - - if (crc32) - { - int len = strlen (result) + 1; - offset = (offset + len + 3) & ~0x3; - - *crc32 = GET_UINT32 (parser, offset); - } - - return result; -} - -static const guchar * -get_section (ElfParser *parser, - const char *name) -{ - const Section *section = find_section (parser, name, SHT_PROGBITS); - - if (section) - return parser->data + section->offset; - else - return NULL; -} - -const guchar * -elf_parser_get_eh_frame (ElfParser *parser) -{ - return get_section (parser, ".eh_frame"); -} - -const guchar * -elf_parser_get_debug_frame (ElfParser *parser) -{ - return get_section (parser, ".debug_frame"); -} - -const char * -elf_parser_get_sym_name (ElfParser *parser, - const ElfSym *sym) -{ - g_return_val_if_fail (parser != NULL, NULL); - - return (char *)(parser->data + parser->sym_strings + - st_name (parser, sym->table, sym->offset)); -} - -gboolean -elf_parser_owns_symbol (ElfParser *parser, - const ElfSym *sym) -{ - ElfSym *first, *last; - - if (!parser->n_symbols) - return FALSE; - - first = parser->symbols; - last = parser->symbols + parser->n_symbols - 1; - - return first <= sym && sym <= last; -} - -gulong -elf_parser_get_sym_address (ElfParser *parser, - const ElfSym *sym) -{ - return sym->address - parser->text_section->load_address; -} - -void -elf_parser_get_sym_address_range (ElfParser *parser, - const ElfSym *sym, - gulong *begin, - gulong *end) -{ - *begin = sym->address - parser->text_section->load_address; - *end = *begin + st_size (parser, sym->table, sym->offset); -} diff --git a/src/libsysprof/elfparser.h b/src/libsysprof/elfparser.h deleted file mode 100644 index fd19e78d..00000000 --- a/src/libsysprof/elfparser.h +++ /dev/null @@ -1,68 +0,0 @@ -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2006, 2007, Soeren Sandmann - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include - -#ifndef NT_GNU_BUILD_ID -#define NT_GNU_BUILD_ID 3 -#endif - -typedef struct ElfSym ElfSym; -typedef struct ElfParser ElfParser; - -ElfParser *elf_parser_new_from_data (const guchar *data, - gsize length); -ElfParser *elf_parser_new_from_mmap (GMappedFile *mapped_file, - GError **err); -ElfParser *elf_parser_new (const char *filename, - GError **err); -void elf_parser_free (ElfParser *parser); -const char *elf_parser_get_debug_link (ElfParser *parser, - guint32 *crc32); -const gchar *elf_parser_get_build_id (ElfParser *parser); -const guchar *elf_parser_get_eh_frame (ElfParser *parser); -const guchar *elf_parser_get_debug_frame (ElfParser *parser); -gulong elf_parser_get_text_offset (ElfParser *parser); - - -/* Lookup a symbol in the file. - * - * The symbol returned is const, so don't free it. It is valid until - * elf_parser_free() is called on the parser. - * - * The address should be given in "file coordinates". This means that - * if the file is mapped at address m and offset o, then an address a - * should be looked up as "a - (m - o)". (m - o) is where the start - * of the file would have been mapped, so a - (m - o) is the position - * in the file of a. - */ -const ElfSym *elf_parser_lookup_symbol (ElfParser *parser, - gulong address); -guint32 elf_parser_get_crc32 (ElfParser *parser); -const char *elf_parser_get_sym_name (ElfParser *parser, - const ElfSym *sym); -gulong elf_parser_get_sym_address (ElfParser *parser, - const ElfSym *sym); -gboolean elf_parser_owns_symbol (ElfParser *parser, - const ElfSym *sym); -char *elf_demangle (const char *name); -void elf_parser_get_sym_address_range (ElfParser *parser, - const ElfSym *sym, - gulong *begin, - gulong *end); - diff --git a/src/libsysprof/mapped-ring-buffer-source.c b/src/libsysprof/mapped-ring-buffer-source.c deleted file mode 100644 index fdeb4b77..00000000 --- a/src/libsysprof/mapped-ring-buffer-source.c +++ /dev/null @@ -1,121 +0,0 @@ -/* mapped-ring-buffer-source.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 G_LOG_DOMAIN "sysprof-mapped-ring-buffer-source" - -#include "config.h" - -#include - -#include "mapped-ring-buffer-source.h" - -typedef struct _MappedRingSource -{ - GSource source; - MappedRingBuffer *buffer; -} MappedRingSource; - -static gboolean -mapped_ring_source_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data) -{ - MappedRingSource *real_source = (MappedRingSource *)source; - - g_assert (source != NULL); - - return mapped_ring_buffer_drain (real_source->buffer, - (MappedRingBufferCallback)callback, - user_data); -} - -static void -mapped_ring_source_finalize (GSource *source) -{ - MappedRingSource *real_source = (MappedRingSource *)source; - - if (real_source != NULL) - g_clear_pointer (&real_source->buffer, mapped_ring_buffer_unref); -} - -static gboolean -mapped_ring_source_check (GSource *source) -{ - MappedRingSource *real_source = (MappedRingSource *)source; - - g_assert (real_source != NULL); - g_assert (real_source->buffer != NULL); - - return !mapped_ring_buffer_is_empty (real_source->buffer); -} - -static gboolean -mapped_ring_source_prepare (GSource *source, - gint *timeout_) -{ - MappedRingSource *real_source = (MappedRingSource *)source; - - g_assert (real_source != NULL); - g_assert (real_source->buffer != NULL); - - if (!mapped_ring_buffer_is_empty (real_source->buffer)) - return TRUE; - - *timeout_ = 5; - - return FALSE; -} - -static GSourceFuncs mapped_ring_source_funcs = { - .prepare = mapped_ring_source_prepare, - .check = mapped_ring_source_check, - .dispatch = mapped_ring_source_dispatch, - .finalize = mapped_ring_source_finalize, -}; - -guint -mapped_ring_buffer_create_source_full (MappedRingBuffer *self, - MappedRingBufferCallback source_func, - gpointer user_data, - GDestroyNotify destroy) -{ - MappedRingSource *source; - guint ret; - - g_return_val_if_fail (self != NULL, 0); - g_return_val_if_fail (source_func != NULL, 0); - - source = (MappedRingSource *)g_source_new (&mapped_ring_source_funcs, sizeof (MappedRingSource)); - source->buffer = mapped_ring_buffer_ref (self); - g_source_set_callback ((GSource *)source, (GSourceFunc)source_func, user_data, destroy); - g_source_set_name ((GSource *)source, "MappedRingSource"); - ret = g_source_attach ((GSource *)source, g_main_context_default ()); - g_source_unref ((GSource *)source); - - return ret; -} - -guint -mapped_ring_buffer_create_source (MappedRingBuffer *self, - MappedRingBufferCallback source_func, - gpointer user_data) -{ - return mapped_ring_buffer_create_source_full (self, source_func, user_data, NULL); -} diff --git a/src/libsysprof/mapped-ring-buffer-source.h b/src/libsysprof/mapped-ring-buffer-source.h deleted file mode 100644 index 167f4c84..00000000 --- a/src/libsysprof/mapped-ring-buffer-source.h +++ /dev/null @@ -1,47 +0,0 @@ -/* mapped-ring-buffer-source.h - * - * 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include - -#include "sysprof-version-macros.h" - -#include "mapped-ring-buffer.h" - -G_BEGIN_DECLS - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (MappedRingBuffer, mapped_ring_buffer_unref) - -G_GNUC_INTERNAL -guint mapped_ring_buffer_create_source (MappedRingBuffer *self, - MappedRingBufferCallback callback, - gpointer user_data); -G_GNUC_INTERNAL -guint mapped_ring_buffer_create_source_full (MappedRingBuffer *self, - MappedRingBufferCallback callback, - gpointer user_data, - GDestroyNotify destroy); - -G_END_DECLS diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build deleted file mode 100644 index 2e3af830..00000000 --- a/src/libsysprof/meson.build +++ /dev/null @@ -1,198 +0,0 @@ -libsysprof_c_args = [ '-DSYSPROF_COMPILATION' ] - -libsysprof_public_sources = [ - 'sysprof-battery-source.c', - 'sysprof-callgraph-profile.c', - 'sysprof-capture-gobject.c', - 'sysprof-capture-symbol-resolver.c', - 'sysprof-control-source.c', - 'sysprof-diskstat-source.c', - 'sysprof-elf-symbol-resolver.c', - 'sysprof-gjs-source.c', - 'sysprof-governor-source.c', - 'sysprof-hostinfo-source.c', - 'sysprof-jitmap-symbol-resolver.c', - 'sysprof-kernel-symbol.c', - 'sysprof-kernel-symbol-resolver.c', - 'sysprof-local-profiler.c', - 'sysprof-memprof-profile.c', - 'sysprof-memprof-source.c', - 'sysprof-netdev-source.c', - 'sysprof-preload-source.c', - 'sysprof-process-model.c', - 'sysprof-process-model-item.c', - 'sysprof-profile.c', - 'sysprof-profiler.c', - 'sysprof-proxy-source.c', - 'sysprof-selection.c', - 'sysprof-source.c', - 'sysprof-spawnable.c', - 'sysprof-symbol-resolver.c', - 'sysprof-symbols-source.c', - 'sysprof-tracefd-source.c', -] - -libsysprof_public_headers = [ - 'sysprof-battery-source.h', - 'sysprof-callgraph-profile.h', - 'sysprof-capture-autocleanups.h', - 'sysprof-capture-gobject.h', - 'sysprof-capture-symbol-resolver.h', - 'sysprof-control-source.h', - 'sysprof-diskstat-source.h', - 'sysprof-elf-symbol-resolver.h', - 'sysprof-gjs-source.h', - 'sysprof-governor-source.h', - 'sysprof-hostinfo-source.h', - 'sysprof-jitmap-symbol-resolver.h', - 'sysprof-netdev-source.h', - 'sysprof-kernel-symbol.h', - 'sysprof-kernel-symbol-resolver.h', - 'sysprof-local-profiler.h', - 'sysprof-memprof-profile.h', - 'sysprof-memprof-source.h', - 'sysprof-preload-source.h', - 'sysprof-process-model.h', - 'sysprof-process-model-item.h', - 'sysprof-profile.h', - 'sysprof-profiler.h', - 'sysprof-proxy-source.h', - 'sysprof-selection.h', - 'sysprof-source.h', - 'sysprof-spawnable.h', - 'sysprof-symbol-resolver.h', - 'sysprof-symbols-source.h', - 'sysprof-tracefd-source.h', - 'sysprof.h', -] - -libsysprof_private_sources = [ - 'binfile.c', - 'demangle.cpp', - 'elfparser.c', - 'mapped-ring-buffer-source.c', - 'sysprof-flatpak.c', - 'sysprof-helpers.c', - 'sysprof-kallsyms.c', - 'sysprof-line-reader.c', - 'sysprof-map-lookaside.c', - 'sysprof-mountinfo.c', - 'sysprof-path-resolver.c', - 'sysprof-podman.c', - 'sysprof-polkit.c', - 'sysprof-symbol-map.c', - ipc_service_src, - stackstash_sources, - helpers_sources, -] - -libsysprof_public_sources += libsysprof_capture_sources - -librax = static_library('rax', ['rax.c'], - c_args: [ '-Wno-declaration-after-statement', - '-Wno-format-nonliteral', - '-Wno-shadow' ], - gnu_symbol_visibility: 'hidden', -) - -librax_dep = declare_dependency( - link_whole: librax, - include_directories: include_directories('.'), -) - -polkit_dep = dependency('polkit-gobject-1', version: polkit_req_version, required: false) -if polkit_dep.found() - libsysprof_c_args += ['-DHAVE_POLKIT'] -endif - -if dependency('polkit-gobject-1', version: '>= 0.114', required: false).found() - libsysprof_c_args += ['-DHAVE_POLKIT_AUTOPTR'] -endif - -# Subset of dependencies used in generating the pkg-config file -libsysprof_pkg_deps = [ - dependency('gio-2.0', version: glib_req_version), - dependency('gio-unix-2.0', version: glib_req_version), - dependency('json-glib-1.0'), - polkit_dep, - libsysprof_capture_deps, -] - -if host_machine.system() == 'linux' - libsysprof_public_sources += [ - 'sysprof-memory-source.c', - 'sysprof-perf-counter.c', - 'sysprof-perf-source.c', - 'sysprof-proc-source.c', - ] - - libsysprof_public_headers += [ - 'sysprof-memory-source.h', - 'sysprof-perf-counter.h', - 'sysprof-perf-source.h', - 'sysprof-proc-source.h', - ] -endif - -if host_machine.system() == 'darwin' - libsysprof_pkg_deps += [ dependency('libelf') ] - libsysprof_c_args += [ '-DNT_GNU_BUILD_ID=3', '-DELF_NOTE_GNU="GNU"', '-D__LIBELF_INTERNAL__' ] -endif - -libsysprof_deps = libsysprof_pkg_deps - -libsysprof_libs_private = [] - -if host_machine.system() != 'darwin' - libsysprof_deps += [cxx.find_library('stdc++')] - libsysprof_libs_private += '-lstdc++' -endif - -libsysprof_static = static_library( - 'sysprof', - (libsysprof_public_sources + - libsysprof_private_sources + - mapped_ring_buffer_sources), - - include_directories: [include_directories('.'), - ipc_include_dirs, - libsysprof_capture_include_dirs], - dependencies: libsysprof_deps, - c_args: libsysprof_c_args, - gnu_symbol_visibility: 'hidden', -) - -libsysprof_static_dep = declare_dependency( - link_whole: libsysprof_static, - dependencies: libsysprof_deps + [librax_dep], - include_directories: [include_directories('.'), libsysprof_capture_include_dirs], -) - -if get_option('libsysprof') -libsysprof = shared_library('sysprof-@0@'.format(libsysprof_api_version), - dependencies: libsysprof_deps + [libsysprof_static_dep], - install: true, - install_dir: get_option('libdir'), -) - -libsysprof_dep = declare_dependency( - link_with: libsysprof, - dependencies: libsysprof_deps, - include_directories: [include_directories('.'), libsysprof_capture_include_dirs], -) -meson.override_dependency('sysprof-@0@'.format(libsysprof_api_version), libsysprof_dep) - -pkgconfig.generate( - libsysprof, - subdirs: [ sysprof_header_subdir ], - description: 'The library for console applications embedding sysprof', - install_dir: join_paths(get_option('libdir'), 'pkgconfig'), - requires: [ 'gio-2.0' ], - libraries_private: [libsysprof_libs_private, libsysprof_pkg_deps], - variables: [ - 'datadir=' + datadir_for_pc_file, - ], -) - -install_headers(libsysprof_public_headers, subdir: sysprof_header_subdir) -endif diff --git a/src/libsysprof/rax.c b/src/libsysprof/rax.c deleted file mode 100644 index 287f9855..00000000 --- a/src/libsysprof/rax.c +++ /dev/null @@ -1,1927 +0,0 @@ -/* Rax -- A radix tree implementation. - * - * Version 1.2 -- 7 February 2019 - * - * Copyright (c) 2017-2019, Salvatore Sanfilippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include "rax.h" - -#ifndef RAX_MALLOC_INCLUDE -#define RAX_MALLOC_INCLUDE "rax_malloc.h" -#endif - -#include RAX_MALLOC_INCLUDE - -/* This is a special pointer that is guaranteed to never have the same value - * of a radix tree node. It's used in order to report "not found" error without - * requiring the function to have multiple return values. */ -void *raxNotFound = (void*)"rax-not-found-pointer"; - -/* -------------------------------- Debugging ------------------------------ */ - -void raxDebugShowNode(const char *msg, raxNode *n); - -/* Turn debugging messages on/off by compiling with RAX_DEBUG_MSG macro on. - * When RAX_DEBUG_MSG is defined by default Rax operations will emit a lot - * of debugging info to the standard output, however you can still turn - * debugging on/off in order to enable it only when you suspect there is an - * operation causing a bug using the function raxSetDebugMsg(). */ -#ifdef RAX_DEBUG_MSG -#define debugf(...) \ - if (raxDebugMsg) { \ - printf("%s:%s:%d:\t", __FILE__, __func__, __LINE__); \ - printf(__VA_ARGS__); \ - fflush(stdout); \ - } - -#define debugnode(msg,n) raxDebugShowNode(msg,n) -#else -#define debugf(...) -#define debugnode(msg,n) -#endif - -/* By default log debug info if RAX_DEBUG_MSG is defined. */ -static int raxDebugMsg = 1; - -/* When debug messages are enabled, turn them on/off dynamically. By - * default they are enabled. Set the state to 0 to disable, and 1 to - * re-enable. */ -void raxSetDebugMsg(int onoff) { - raxDebugMsg = onoff; -} - -/* ------------------------- raxStack functions -------------------------- - * The raxStack is a simple stack of pointers that is capable of switching - * from using a stack-allocated array to dynamic heap once a given number of - * items are reached. It is used in order to retain the list of parent nodes - * while walking the radix tree in order to implement certain operations that - * need to navigate the tree upward. - * ------------------------------------------------------------------------- */ - -/* Initialize the stack. */ -static inline void raxStackInit(raxStack *ts) { - ts->stack = ts->static_items; - ts->items = 0; - ts->maxitems = RAX_STACK_STATIC_ITEMS; - ts->oom = 0; -} - -/* Push an item into the stack, returns 1 on success, 0 on out of memory. */ -static inline int raxStackPush(raxStack *ts, void *ptr) { - if (ts->items == ts->maxitems) { - if (ts->stack == ts->static_items) { - ts->stack = rax_malloc(sizeof(void*)*ts->maxitems*2); - if (ts->stack == NULL) { - ts->stack = ts->static_items; - ts->oom = 1; - errno = ENOMEM; - return 0; - } - memcpy(ts->stack,ts->static_items,sizeof(void*)*ts->maxitems); - } else { - void **newalloc = rax_realloc(ts->stack,sizeof(void*)*ts->maxitems*2); - if (newalloc == NULL) { - ts->oom = 1; - errno = ENOMEM; - return 0; - } - ts->stack = newalloc; - } - ts->maxitems *= 2; - } - ts->stack[ts->items] = ptr; - ts->items++; - return 1; -} - -/* Pop an item from the stack, the function returns NULL if there are no - * items to pop. */ -static inline void *raxStackPop(raxStack *ts) { - if (ts->items == 0) return NULL; - ts->items--; - return ts->stack[ts->items]; -} - -/* Return the stack item at the top of the stack without actually consuming - * it. */ -static inline void *raxStackPeek(raxStack *ts) { - if (ts->items == 0) return NULL; - return ts->stack[ts->items-1]; -} - -/* Free the stack in case we used heap allocation. */ -static inline void raxStackFree(raxStack *ts) { - if (ts->stack != ts->static_items) rax_free(ts->stack); -} - -/* ---------------------------------------------------------------------------- - * Radix tree implementation - * --------------------------------------------------------------------------*/ - -/* Return the padding needed in the characters section of a node having size - * 'nodesize'. The padding is needed to store the child pointers to aligned - * addresses. Note that we add 4 to the node size because the node has a four - * bytes header. */ -#define raxPadding(nodesize) ((sizeof(void*)-(((nodesize)+4) % sizeof(void*))) & (sizeof(void*)-1)) - -/* Return the pointer to the last child pointer in a node. For the compressed - * nodes this is the only child pointer. */ -#define raxNodeLastChildPtr(n) ((raxNode**) ( \ - ((char*)(n)) + \ - raxNodeCurrentLength(n) - \ - sizeof(raxNode*) - \ - (((n)->iskey && !(n)->isnull) ? sizeof(void*) : 0) \ -)) - -/* Return the pointer to the first child pointer. */ -#define raxNodeFirstChildPtr(n) ((raxNode**) ( \ - (n)->data + \ - (n)->size + \ - raxPadding((n)->size))) - -/* Return the current total size of the node. Note that the second line - * computes the padding after the string of characters, needed in order to - * save pointers to aligned addresses. */ -#define raxNodeCurrentLength(n) ( \ - sizeof(raxNode)+(n)->size+ \ - raxPadding((n)->size)+ \ - ((n)->iscompr ? sizeof(raxNode*) : sizeof(raxNode*)*(n)->size)+ \ - (((n)->iskey && !(n)->isnull)*sizeof(void*)) \ -) - -/* Allocate a new non compressed node with the specified number of children. - * If datafield is true, the allocation is made large enough to hold the - * associated data pointer. - * Returns the new node pointer. On out of memory NULL is returned. */ -raxNode *raxNewNode(size_t children, int datafield) { - size_t nodesize = sizeof(raxNode)+children+raxPadding(children)+ - sizeof(raxNode*)*children; - if (datafield) nodesize += sizeof(void*); - raxNode *node = rax_malloc(nodesize); - if (node == NULL) return NULL; - node->iskey = 0; - node->isnull = 0; - node->iscompr = 0; - node->size = children; - return node; -} - -/* Allocate a new rax and return its pointer. On out of memory the function - * returns NULL. */ -rax *raxNew(void) { - rax *rax = rax_malloc(sizeof(*rax)); - if (rax == NULL) return NULL; - rax->numele = 0; - rax->numnodes = 1; - rax->head = raxNewNode(0,0); - if (rax->head == NULL) { - rax_free(rax); - return NULL; - } else { - return rax; - } -} - -/* realloc the node to make room for auxiliary data in order - * to store an item in that node. On out of memory NULL is returned. */ -raxNode *raxReallocForData(raxNode *n, void *data) { - if (data == NULL) return n; /* No reallocation needed, setting isnull=1 */ - size_t curlen = raxNodeCurrentLength(n); - return rax_realloc(n,curlen+sizeof(void*)); -} - -/* Set the node auxiliary data to the specified pointer. */ -void raxSetData(raxNode *n, void *data) { - n->iskey = 1; - if (data != NULL) { - n->isnull = 0; - void **ndata = (void**) - ((char*)n+raxNodeCurrentLength(n)-sizeof(void*)); - memcpy(ndata,&data,sizeof(data)); - } else { - n->isnull = 1; - } -} - -/* Get the node auxiliary data. */ -void *raxGetData(raxNode *n) { - if (n->isnull) return NULL; - void **ndata =(void**)((char*)n+raxNodeCurrentLength(n)-sizeof(void*)); - void *data; - memcpy(&data,ndata,sizeof(data)); - return data; -} - -/* Add a new child to the node 'n' representing the character 'c' and return - * its new pointer, as well as the child pointer by reference. Additionally - * '***parentlink' is populated with the raxNode pointer-to-pointer of where - * the new child was stored, which is useful for the caller to replace the - * child pointer if it gets reallocated. - * - * On success the new parent node pointer is returned (it may change because - * of the realloc, so the caller should discard 'n' and use the new value). - * On out of memory NULL is returned, and the old node is still valid. */ -raxNode *raxAddChild(raxNode *n, unsigned char c, raxNode **childptr, raxNode ***parentlink) { - assert(n->iscompr == 0); - - size_t curlen = raxNodeCurrentLength(n); - n->size++; - size_t newlen = raxNodeCurrentLength(n); - n->size--; /* For now restore the original size. We'll update it only on - success at the end. */ - - /* Alloc the new child we will link to 'n'. */ - raxNode *child = raxNewNode(0,0); - if (child == NULL) return NULL; - - /* Make space in the original node. */ - raxNode *newn = rax_realloc(n,newlen); - if (newn == NULL) { - rax_free(child); - return NULL; - } - n = newn; - - /* After the reallocation, we have up to 8/16 (depending on the system - * pointer size, and the required node padding) bytes at the end, that is, - * the additional char in the 'data' section, plus one pointer to the new - * child, plus the padding needed in order to store addresses into aligned - * locations. - * - * So if we start with the following node, having "abde" edges. - * - * Note: - * - We assume 4 bytes pointer for simplicity. - * - Each space below corresponds to one byte - * - * [HDR*][abde][Aptr][Bptr][Dptr][Eptr]|AUXP| - * - * After the reallocation we need: 1 byte for the new edge character - * plus 4 bytes for a new child pointer (assuming 32 bit machine). - * However after adding 1 byte to the edge char, the header + the edge - * characters are no longer aligned, so we also need 3 bytes of padding. - * In total the reallocation will add 1+4+3 bytes = 8 bytes: - * - * (Blank bytes are represented by ".") - * - * [HDR*][abde][Aptr][Bptr][Dptr][Eptr]|AUXP|[....][....] - * - * Let's find where to insert the new child in order to make sure - * it is inserted in-place lexicographically. Assuming we are adding - * a child "c" in our case pos will be = 2 after the end of the following - * loop. */ - int pos; - for (pos = 0; pos < n->size; pos++) { - if (n->data[pos] > c) break; - } - - /* Now, if present, move auxiliary data pointer at the end - * so that we can mess with the other data without overwriting it. - * We will obtain something like that: - * - * [HDR*][abde][Aptr][Bptr][Dptr][Eptr][....][....]|AUXP| - */ - unsigned char *src, *dst; - if (n->iskey && !n->isnull) { - src = ((unsigned char*)n+curlen-sizeof(void*)); - dst = ((unsigned char*)n+newlen-sizeof(void*)); - memmove(dst,src,sizeof(void*)); - } - - /* Compute the "shift", that is, how many bytes we need to move the - * pointers section forward because of the addition of the new child - * byte in the string section. Note that if we had no padding, that - * would be always "1", since we are adding a single byte in the string - * section of the node (where now there is "abde" basically). - * - * However we have padding, so it could be zero, or up to 8. - * - * Another way to think at the shift is, how many bytes we need to - * move child pointers forward *other than* the obvious sizeof(void*) - * needed for the additional pointer itself. */ - size_t shift = newlen - curlen - sizeof(void*); - - /* We said we are adding a node with edge 'c'. The insertion - * point is between 'b' and 'd', so the 'pos' variable value is - * the index of the first child pointer that we need to move forward - * to make space for our new pointer. - * - * To start, move all the child pointers after the insertion point - * of shift+sizeof(pointer) bytes on the right, to obtain: - * - * [HDR*][abde][Aptr][Bptr][....][....][Dptr][Eptr]|AUXP| - */ - src = n->data+n->size+ - raxPadding(n->size)+ - sizeof(raxNode*)*pos; - memmove(src+shift+sizeof(raxNode*),src,sizeof(raxNode*)*(n->size-pos)); - - /* Move the pointers to the left of the insertion position as well. Often - * we don't need to do anything if there was already some padding to use. In - * that case the final destination of the pointers will be the same, however - * in our example there was no pre-existing padding, so we added one byte - * plus three bytes of padding. After the next memmove() things will look - * like that: - * - * [HDR*][abde][....][Aptr][Bptr][....][Dptr][Eptr]|AUXP| - */ - if (shift) { - src = (unsigned char*) raxNodeFirstChildPtr(n); - memmove(src+shift,src,sizeof(raxNode*)*pos); - } - - /* Now make the space for the additional char in the data section, - * but also move the pointers before the insertion point to the right - * by shift bytes, in order to obtain the following: - * - * [HDR*][ab.d][e...][Aptr][Bptr][....][Dptr][Eptr]|AUXP| - */ - src = n->data+pos; - memmove(src+1,src,n->size-pos); - - /* We can now set the character and its child node pointer to get: - * - * [HDR*][abcd][e...][Aptr][Bptr][....][Dptr][Eptr]|AUXP| - * [HDR*][abcd][e...][Aptr][Bptr][Cptr][Dptr][Eptr]|AUXP| - */ - n->data[pos] = c; - n->size++; - src = (unsigned char*) raxNodeFirstChildPtr(n); - raxNode **childfield = (raxNode**)(src+sizeof(raxNode*)*pos); - memcpy(childfield,&child,sizeof(child)); - *childptr = child; - *parentlink = childfield; - return n; -} - -/* Turn the node 'n', that must be a node without any children, into a - * compressed node representing a set of nodes linked one after the other - * and having exactly one child each. The node can be a key or not: this - * property and the associated value if any will be preserved. - * - * The function also returns a child node, since the last node of the - * compressed chain cannot be part of the chain: it has zero children while - * we can only compress inner nodes with exactly one child each. */ -raxNode *raxCompressNode(raxNode *n, unsigned char *s, size_t len, raxNode **child) { - assert(n->size == 0 && n->iscompr == 0); - void *data = NULL; /* Initialized only to avoid warnings. */ - size_t newsize; - - debugf("Compress node: %.*s\n", (int)len,s); - - /* Allocate the child to link to this node. */ - *child = raxNewNode(0,0); - if (*child == NULL) return NULL; - - /* Make space in the parent node. */ - newsize = sizeof(raxNode)+len+raxPadding(len)+sizeof(raxNode*); - if (n->iskey) { - data = raxGetData(n); /* To restore it later. */ - if (!n->isnull) newsize += sizeof(void*); - } - raxNode *newn = rax_realloc(n,newsize); - if (newn == NULL) { - rax_free(*child); - return NULL; - } - n = newn; - - n->iscompr = 1; - n->size = len; - memcpy(n->data,s,len); - if (n->iskey) raxSetData(n,data); - raxNode **childfield = raxNodeLastChildPtr(n); - memcpy(childfield,child,sizeof(*child)); - return n; -} - -/* Low level function that walks the tree looking for the string - * 's' of 'len' bytes. The function returns the number of characters - * of the key that was possible to process: if the returned integer - * is the same as 'len', then it means that the node corresponding to the - * string was found (however it may not be a key in case the node->iskey is - * zero or if simply we stopped in the middle of a compressed node, so that - * 'splitpos' is non zero). - * - * Otherwise if the returned integer is not the same as 'len', there was an - * early stop during the tree walk because of a character mismatch. - * - * The node where the search ended (because the full string was processed - * or because there was an early stop) is returned by reference as - * '*stopnode' if the passed pointer is not NULL. This node link in the - * parent's node is returned as '*plink' if not NULL. Finally, if the - * search stopped in a compressed node, '*splitpos' returns the index - * inside the compressed node where the search ended. This is useful to - * know where to split the node for insertion. - * - * Note that when we stop in the middle of a compressed node with - * a perfect match, this function will return a length equal to the - * 'len' argument (all the key matched), and will return a *splitpos which is - * always positive (that will represent the index of the character immediately - * *after* the last match in the current compressed node). - * - * When instead we stop at a compressed node and *splitpos is zero, it - * means that the current node represents the key (that is, none of the - * compressed node characters are needed to represent the key, just all - * its parents nodes). */ -static inline size_t raxLowWalk(rax *rax, unsigned char *s, size_t len, raxNode **stopnode, raxNode ***plink, int *splitpos, raxStack *ts) { - raxNode *h = rax->head; - raxNode **parentlink = &rax->head; - - size_t i = 0; /* Position in the string. */ - size_t j = 0; /* Position in the node children (or bytes if compressed).*/ - while(h->size && i < len) { - debugnode("Lookup current node",h); - unsigned char *v = h->data; - - if (h->iscompr) { - for (j = 0; j < h->size && i < len; j++, i++) { - if (v[j] != s[i]) break; - } - if (j != h->size) break; - } else { - /* Even when h->size is large, linear scan provides good - * performances compared to other approaches that are in theory - * more sounding, like performing a binary search. */ - for (j = 0; j < h->size; j++) { - if (v[j] == s[i]) break; - } - if (j == h->size) break; - i++; - } - - if (ts) raxStackPush(ts,h); /* Save stack of parent nodes. */ - raxNode **children = raxNodeFirstChildPtr(h); - if (h->iscompr) j = 0; /* Compressed node only child is at index 0. */ - memcpy(&h,children+j,sizeof(h)); - parentlink = children+j; - j = 0; /* If the new node is non compressed and we do not - iterate again (since i == len) set the split - position to 0 to signal this node represents - the searched key. */ - } - debugnode("Lookup stop node is",h); - if (stopnode) *stopnode = h; - if (plink) *plink = parentlink; - if (splitpos && h->iscompr) *splitpos = j; - return i; -} - -/* Insert the element 's' of size 'len', setting as auxiliary data - * the pointer 'data'. If the element is already present, the associated - * data is updated (only if 'overwrite' is set to 1), and 0 is returned, - * otherwise the element is inserted and 1 is returned. On out of memory the - * function returns 0 as well but sets errno to ENOMEM, otherwise errno will - * be set to 0. - */ -int raxGenericInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old, int overwrite) { - size_t i; - int j = 0; /* Split position. If raxLowWalk() stops in a compressed - node, the index 'j' represents the char we stopped within the - compressed node, that is, the position where to split the - node for insertion. */ - raxNode *h, **parentlink; - - debugf("### Insert %.*s with value %p\n", (int)len, s, data); - i = raxLowWalk(rax,s,len,&h,&parentlink,&j,NULL); - - /* If i == len we walked following the whole string. If we are not - * in the middle of a compressed node, the string is either already - * inserted or this middle node is currently not a key, but can represent - * our key. We have just to reallocate the node and make space for the - * data pointer. */ - if (i == len && (!h->iscompr || j == 0 /* not in the middle if j is 0 */)) { - debugf("### Insert: node representing key exists\n"); - /* Make space for the value pointer if needed. */ - if (!h->iskey || (h->isnull && overwrite)) { - h = raxReallocForData(h,data); - if (h) memcpy(parentlink,&h,sizeof(h)); - } - if (h == NULL) { - errno = ENOMEM; - return 0; - } - - /* Update the existing key if there is already one. */ - if (h->iskey) { - if (old) *old = raxGetData(h); - if (overwrite) raxSetData(h,data); - errno = 0; - return 0; /* Element already exists. */ - } - - /* Otherwise set the node as a key. Note that raxSetData() - * will set h->iskey. */ - raxSetData(h,data); - rax->numele++; - return 1; /* Element inserted. */ - } - - /* If the node we stopped at is a compressed node, we need to - * split it before to continue. - * - * Splitting a compressed node have a few possible cases. - * Imagine that the node 'h' we are currently at is a compressed - * node containing the string "ANNIBALE" (it means that it represents - * nodes A -> N -> N -> I -> B -> A -> L -> E with the only child - * pointer of this node pointing at the 'E' node, because remember that - * we have characters at the edges of the graph, not inside the nodes - * themselves. - * - * In order to show a real case imagine our node to also point to - * another compressed node, that finally points at the node without - * children, representing 'O': - * - * "ANNIBALE" -> "SCO" -> [] - * - * When inserting we may face the following cases. Note that all the cases - * require the insertion of a non compressed node with exactly two - * children, except for the last case which just requires splitting a - * compressed node. - * - * 1) Inserting "ANNIENTARE" - * - * |B| -> "ALE" -> "SCO" -> [] - * "ANNI" -> |-| - * |E| -> (... continue algo ...) "NTARE" -> [] - * - * 2) Inserting "ANNIBALI" - * - * |E| -> "SCO" -> [] - * "ANNIBAL" -> |-| - * |I| -> (... continue algo ...) [] - * - * 3) Inserting "AGO" (Like case 1, but set iscompr = 0 into original node) - * - * |N| -> "NIBALE" -> "SCO" -> [] - * |A| -> |-| - * |G| -> (... continue algo ...) |O| -> [] - * - * 4) Inserting "CIAO" - * - * |A| -> "NNIBALE" -> "SCO" -> [] - * |-| - * |C| -> (... continue algo ...) "IAO" -> [] - * - * 5) Inserting "ANNI" - * - * "ANNI" -> "BALE" -> "SCO" -> [] - * - * The final algorithm for insertion covering all the above cases is as - * follows. - * - * ============================= ALGO 1 ============================= - * - * For the above cases 1 to 4, that is, all cases where we stopped in - * the middle of a compressed node for a character mismatch, do: - * - * Let $SPLITPOS be the zero-based index at which, in the - * compressed node array of characters, we found the mismatching - * character. For example if the node contains "ANNIBALE" and we add - * "ANNIENTARE" the $SPLITPOS is 4, that is, the index at which the - * mismatching character is found. - * - * 1. Save the current compressed node $NEXT pointer (the pointer to the - * child element, that is always present in compressed nodes). - * - * 2. Create "split node" having as child the non common letter - * at the compressed node. The other non common letter (at the key) - * will be added later as we continue the normal insertion algorithm - * at step "6". - * - * 3a. IF $SPLITPOS == 0: - * Replace the old node with the split node, by copying the auxiliary - * data if any. Fix parent's reference. Free old node eventually - * (we still need its data for the next steps of the algorithm). - * - * 3b. IF $SPLITPOS != 0: - * Trim the compressed node (reallocating it as well) in order to - * contain $splitpos characters. Change child pointer in order to link - * to the split node. If new compressed node len is just 1, set - * iscompr to 0 (layout is the same). Fix parent's reference. - * - * 4a. IF the postfix len (the length of the remaining string of the - * original compressed node after the split character) is non zero, - * create a "postfix node". If the postfix node has just one character - * set iscompr to 0, otherwise iscompr to 1. Set the postfix node - * child pointer to $NEXT. - * - * 4b. IF the postfix len is zero, just use $NEXT as postfix pointer. - * - * 5. Set child[0] of split node to postfix node. - * - * 6. Set the split node as the current node, set current index at child[1] - * and continue insertion algorithm as usually. - * - * ============================= ALGO 2 ============================= - * - * For case 5, that is, if we stopped in the middle of a compressed - * node but no mismatch was found, do: - * - * Let $SPLITPOS be the zero-based index at which, in the - * compressed node array of characters, we stopped iterating because - * there were no more keys character to match. So in the example of - * the node "ANNIBALE", adding the string "ANNI", the $SPLITPOS is 4. - * - * 1. Save the current compressed node $NEXT pointer (the pointer to the - * child element, that is always present in compressed nodes). - * - * 2. Create a "postfix node" containing all the characters from $SPLITPOS - * to the end. Use $NEXT as the postfix node child pointer. - * If the postfix node length is 1, set iscompr to 0. - * Set the node as a key with the associated value of the new - * inserted key. - * - * 3. Trim the current node to contain the first $SPLITPOS characters. - * As usually if the new node length is just 1, set iscompr to 0. - * Take the iskey / associated value as it was in the original node. - * Fix the parent's reference. - * - * 4. Set the postfix node as the only child pointer of the trimmed - * node created at step 1. - */ - - /* ------------------------- ALGORITHM 1 --------------------------- */ - if (h->iscompr && i != len) { - debugf("ALGO 1: Stopped at compressed node %.*s (%p)\n", - h->size, h->data, (void*)h); - debugf("Still to insert: %.*s\n", (int)(len-i), s+i); - debugf("Splitting at %d: '%c'\n", j, ((char*)h->data)[j]); - debugf("Other (key) letter is '%c'\n", s[i]); - - /* 1: Save next pointer. */ - raxNode **childfield = raxNodeLastChildPtr(h); - raxNode *next; - memcpy(&next,childfield,sizeof(next)); - debugf("Next is %p\n", (void*)next); - debugf("iskey %d\n", h->iskey); - if (h->iskey) { - debugf("key value is %p\n", raxGetData(h)); - } - - /* Set the length of the additional nodes we will need. */ - size_t trimmedlen = j; - size_t postfixlen = h->size - j - 1; - int split_node_is_key = !trimmedlen && h->iskey && !h->isnull; - size_t nodesize; - - /* 2: Create the split node. Also allocate the other nodes we'll need - * ASAP, so that it will be simpler to handle OOM. */ - raxNode *splitnode = raxNewNode(1, split_node_is_key); - raxNode *trimmed = NULL; - raxNode *postfix = NULL; - - if (trimmedlen) { - nodesize = sizeof(raxNode)+trimmedlen+raxPadding(trimmedlen)+ - sizeof(raxNode*); - if (h->iskey && !h->isnull) nodesize += sizeof(void*); - trimmed = rax_malloc(nodesize); - } - - if (postfixlen) { - nodesize = sizeof(raxNode)+postfixlen+raxPadding(postfixlen)+ - sizeof(raxNode*); - postfix = rax_malloc(nodesize); - } - - /* OOM? Abort now that the tree is untouched. */ - if (splitnode == NULL || - (trimmedlen && trimmed == NULL) || - (postfixlen && postfix == NULL)) - { - rax_free(splitnode); - rax_free(trimmed); - rax_free(postfix); - errno = ENOMEM; - return 0; - } - splitnode->data[0] = h->data[j]; - - if (j == 0) { - /* 3a: Replace the old node with the split node. */ - if (h->iskey) { - void *ndata = raxGetData(h); - raxSetData(splitnode,ndata); - } - memcpy(parentlink,&splitnode,sizeof(splitnode)); - } else { - /* 3b: Trim the compressed node. */ - trimmed->size = j; - memcpy(trimmed->data,h->data,j); - trimmed->iscompr = j > 1 ? 1 : 0; - trimmed->iskey = h->iskey; - trimmed->isnull = h->isnull; - if (h->iskey && !h->isnull) { - void *ndata = raxGetData(h); - raxSetData(trimmed,ndata); - } - raxNode **cp = raxNodeLastChildPtr(trimmed); - memcpy(cp,&splitnode,sizeof(splitnode)); - memcpy(parentlink,&trimmed,sizeof(trimmed)); - parentlink = cp; /* Set parentlink to splitnode parent. */ - rax->numnodes++; - } - - /* 4: Create the postfix node: what remains of the original - * compressed node after the split. */ - if (postfixlen) { - /* 4a: create a postfix node. */ - postfix->iskey = 0; - postfix->isnull = 0; - postfix->size = postfixlen; - postfix->iscompr = postfixlen > 1; - memcpy(postfix->data,h->data+j+1,postfixlen); - raxNode **cp = raxNodeLastChildPtr(postfix); - memcpy(cp,&next,sizeof(next)); - rax->numnodes++; - } else { - /* 4b: just use next as postfix node. */ - postfix = next; - } - - /* 5: Set splitnode first child as the postfix node. */ - raxNode **splitchild = raxNodeLastChildPtr(splitnode); - memcpy(splitchild,&postfix,sizeof(postfix)); - - /* 6. Continue insertion: this will cause the splitnode to - * get a new child (the non common character at the currently - * inserted key). */ - rax_free(h); - h = splitnode; - } else if (h->iscompr && i == len) { - /* ------------------------- ALGORITHM 2 --------------------------- */ - debugf("ALGO 2: Stopped at compressed node %.*s (%p) j = %d\n", - h->size, h->data, (void*)h, j); - - /* Allocate postfix & trimmed nodes ASAP to fail for OOM gracefully. */ - size_t postfixlen = h->size - j; - size_t nodesize = sizeof(raxNode)+postfixlen+raxPadding(postfixlen)+ - sizeof(raxNode*); - if (data != NULL) nodesize += sizeof(void*); - raxNode *postfix = rax_malloc(nodesize); - - nodesize = sizeof(raxNode)+j+raxPadding(j)+sizeof(raxNode*); - if (h->iskey && !h->isnull) nodesize += sizeof(void*); - raxNode *trimmed = rax_malloc(nodesize); - - if (postfix == NULL || trimmed == NULL) { - rax_free(postfix); - rax_free(trimmed); - errno = ENOMEM; - return 0; - } - - /* 1: Save next pointer. */ - raxNode **childfield = raxNodeLastChildPtr(h); - raxNode *next; - memcpy(&next,childfield,sizeof(next)); - - /* 2: Create the postfix node. */ - postfix->size = postfixlen; - postfix->iscompr = postfixlen > 1; - postfix->iskey = 1; - postfix->isnull = 0; - memcpy(postfix->data,h->data+j,postfixlen); - raxSetData(postfix,data); - raxNode **cp = raxNodeLastChildPtr(postfix); - memcpy(cp,&next,sizeof(next)); - rax->numnodes++; - - /* 3: Trim the compressed node. */ - trimmed->size = j; - trimmed->iscompr = j > 1; - trimmed->iskey = 0; - trimmed->isnull = 0; - memcpy(trimmed->data,h->data,j); - memcpy(parentlink,&trimmed,sizeof(trimmed)); - if (h->iskey) { - void *aux = raxGetData(h); - raxSetData(trimmed,aux); - } - - /* Fix the trimmed node child pointer to point to - * the postfix node. */ - cp = raxNodeLastChildPtr(trimmed); - memcpy(cp,&postfix,sizeof(postfix)); - - /* Finish! We don't need to continue with the insertion - * algorithm for ALGO 2. The key is already inserted. */ - rax->numele++; - rax_free(h); - return 1; /* Key inserted. */ - } - - /* We walked the radix tree as far as we could, but still there are left - * chars in our string. We need to insert the missing nodes. */ - while(i < len) { - raxNode *child; - - /* If this node is going to have a single child, and there - * are other characters, so that that would result in a chain - * of single-childed nodes, turn it into a compressed node. */ - if (h->size == 0 && len-i > 1) { - debugf("Inserting compressed node\n"); - size_t comprsize = len-i; - if (comprsize > RAX_NODE_MAX_SIZE) - comprsize = RAX_NODE_MAX_SIZE; - raxNode *newh = raxCompressNode(h,s+i,comprsize,&child); - if (newh == NULL) goto oom; - h = newh; - memcpy(parentlink,&h,sizeof(h)); - parentlink = raxNodeLastChildPtr(h); - i += comprsize; - } else { - debugf("Inserting normal node\n"); - raxNode **new_parentlink; - raxNode *newh = raxAddChild(h,s[i],&child,&new_parentlink); - if (newh == NULL) goto oom; - h = newh; - memcpy(parentlink,&h,sizeof(h)); - parentlink = new_parentlink; - i++; - } - rax->numnodes++; - h = child; - } - raxNode *newh = raxReallocForData(h,data); - if (newh == NULL) goto oom; - h = newh; - if (!h->iskey) rax->numele++; - raxSetData(h,data); - memcpy(parentlink,&h,sizeof(h)); - return 1; /* Element inserted. */ - -oom: - /* This code path handles out of memory after part of the sub-tree was - * already modified. Set the node as a key, and then remove it. However we - * do that only if the node is a terminal node, otherwise if the OOM - * happened reallocating a node in the middle, we don't need to free - * anything. */ - if (h->size == 0) { - h->isnull = 1; - h->iskey = 1; - rax->numele++; /* Compensate the next remove. */ - assert(raxRemove(rax,s,i,NULL) != 0); - } - errno = ENOMEM; - return 0; -} - -/* Overwriting insert. Just a wrapper for raxGenericInsert() that will - * update the element if there is already one for the same key. */ -int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) { - return raxGenericInsert(rax,s,len,data,old,1); -} - -/* Non overwriting insert function: if an element with the same key - * exists, the value is not updated and the function returns 0. - * This is just a wrapper for raxGenericInsert(). */ -int raxTryInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) { - return raxGenericInsert(rax,s,len,data,old,0); -} - -/* Find a key in the rax, returns raxNotFound special void pointer value - * if the item was not found, otherwise the value associated with the - * item is returned. */ -void *raxFind(rax *rax, unsigned char *s, size_t len) { - raxNode *h; - - debugf("### Lookup: %.*s\n", (int)len, s); - int splitpos = 0; - size_t i = raxLowWalk(rax,s,len,&h,NULL,&splitpos,NULL); - if (i != len || (h->iscompr && splitpos != 0) || !h->iskey) - return raxNotFound; - return raxGetData(h); -} - -/* Return the memory address where the 'parent' node stores the specified - * 'child' pointer, so that the caller can update the pointer with another - * one if needed. The function assumes it will find a match, otherwise the - * operation is an undefined behavior (it will continue scanning the - * memory without any bound checking). */ -raxNode **raxFindParentLink(raxNode *parent, raxNode *child) { - raxNode **cp = raxNodeFirstChildPtr(parent); - raxNode *c; - while(1) { - memcpy(&c,cp,sizeof(c)); - if (c == child) break; - cp++; - } - return cp; -} - -/* Low level child removal from node. The new node pointer (after the child - * removal) is returned. Note that this function does not fix the pointer - * of the parent node in its parent, so this task is up to the caller. - * The function never fails for out of memory. */ -raxNode *raxRemoveChild(raxNode *parent, raxNode *child) { - debugnode("raxRemoveChild before", parent); - /* If parent is a compressed node (having a single child, as for definition - * of the data structure), the removal of the child consists into turning - * it into a normal node without children. */ - if (parent->iscompr) { - void *data = NULL; - if (parent->iskey) data = raxGetData(parent); - parent->isnull = 0; - parent->iscompr = 0; - parent->size = 0; - if (parent->iskey) raxSetData(parent,data); - debugnode("raxRemoveChild after", parent); - return parent; - } - - /* Otherwise we need to scan for the child pointer and memmove() - * accordingly. - * - * 1. To start we seek the first element in both the children - * pointers and edge bytes in the node. */ - raxNode **cp = raxNodeFirstChildPtr(parent); - raxNode **c = cp; - unsigned char *e = parent->data; - - /* 2. Search the child pointer to remove inside the array of children - * pointers. */ - while(1) { - raxNode *aux; - memcpy(&aux,c,sizeof(aux)); - if (aux == child) break; - c++; - e++; - } - - /* 3. Remove the edge and the pointer by memmoving the remaining children - * pointer and edge bytes one position before. */ - int taillen = parent->size - (e - parent->data) - 1; - debugf("raxRemoveChild tail len: %d\n", taillen); - memmove(e,e+1,taillen); - - /* Compute the shift, that is the amount of bytes we should move our - * child pointers to the left, since the removal of one edge character - * and the corresponding padding change, may change the layout. - * We just check if in the old version of the node there was at the - * end just a single byte and all padding: in that case removing one char - * will remove a whole sizeof(void*) word. */ - size_t shift = ((parent->size+4) % sizeof(void*)) == 1 ? sizeof(void*) : 0; - - /* Move the children pointers before the deletion point. */ - if (shift) - memmove(((char*)cp)-shift,cp,(parent->size-taillen-1)*sizeof(raxNode**)); - - /* Move the remaining "tail" pointers at the right position as well. */ - size_t valuelen = (parent->iskey && !parent->isnull) ? sizeof(void*) : 0; - memmove(((char*)c)-shift,c+1,taillen*sizeof(raxNode**)+valuelen); - - /* 4. Update size. */ - parent->size--; - - /* realloc the node according to the theoretical memory usage, to free - * data if we are over-allocating right now. */ - raxNode *newnode = rax_realloc(parent,raxNodeCurrentLength(parent)); - if (newnode) { - debugnode("raxRemoveChild after", newnode); - } - /* Note: if rax_realloc() fails we just return the old address, which - * is valid. */ - return newnode ? newnode : parent; -} - -/* Remove the specified item. Returns 1 if the item was found and - * deleted, 0 otherwise. */ -int raxRemove(rax *rax, unsigned char *s, size_t len, void **old) { - raxNode *h; - raxStack ts; - - debugf("### Delete: %.*s\n", (int)len, s); - raxStackInit(&ts); - int splitpos = 0; - size_t i = raxLowWalk(rax,s,len,&h,NULL,&splitpos,&ts); - if (i != len || (h->iscompr && splitpos != 0) || !h->iskey) { - raxStackFree(&ts); - return 0; - } - if (old) *old = raxGetData(h); - h->iskey = 0; - rax->numele--; - - /* If this node has no children, the deletion needs to reclaim the - * no longer used nodes. This is an iterative process that needs to - * walk the three upward, deleting all the nodes with just one child - * that are not keys, until the head of the rax is reached or the first - * node with more than one child is found. */ - - int trycompress = 0; /* Will be set to 1 if we should try to optimize the - tree resulting from the deletion. */ - - if (h->size == 0) { - debugf("Key deleted in node without children. Cleanup needed.\n"); - raxNode *child = NULL; - while(h != rax->head) { - child = h; - debugf("Freeing child %p [%.*s] key:%d\n", (void*)child, - (int)child->size, (char*)child->data, child->iskey); - rax_free(child); - rax->numnodes--; - h = raxStackPop(&ts); - /* If this node has more then one child, or actually holds - * a key, stop here. */ - if (h->iskey || (!h->iscompr && h->size != 1)) break; - } - if (child) { - debugf("Unlinking child %p from parent %p\n", - (void*)child, (void*)h); - raxNode *new = raxRemoveChild(h,child); - if (new != h) { - raxNode *parent = raxStackPeek(&ts); - raxNode **parentlink; - if (parent == NULL) { - parentlink = &rax->head; - } else { - parentlink = raxFindParentLink(parent,h); - } - memcpy(parentlink,&new,sizeof(new)); - } - - /* If after the removal the node has just a single child - * and is not a key, we need to try to compress it. */ - if (new->size == 1 && new->iskey == 0) { - trycompress = 1; - h = new; - } - } - } else if (h->size == 1) { - /* If the node had just one child, after the removal of the key - * further compression with adjacent nodes is potentially possible. */ - trycompress = 1; - } - - /* Don't try node compression if our nodes pointers stack is not - * complete because of OOM while executing raxLowWalk() */ - if (trycompress && ts.oom) trycompress = 0; - - /* Recompression: if trycompress is true, 'h' points to a radix tree node - * that changed in a way that could allow to compress nodes in this - * sub-branch. Compressed nodes represent chains of nodes that are not - * keys and have a single child, so there are two deletion events that - * may alter the tree so that further compression is needed: - * - * 1) A node with a single child was a key and now no longer is a key. - * 2) A node with two children now has just one child. - * - * We try to navigate upward till there are other nodes that can be - * compressed, when we reach the upper node which is not a key and has - * a single child, we scan the chain of children to collect the - * compressible part of the tree, and replace the current node with the - * new one, fixing the child pointer to reference the first non - * compressible node. - * - * Example of case "1". A tree stores the keys "FOO" = 1 and - * "FOOBAR" = 2: - * - * - * "FOO" -> "BAR" -> [] (2) - * (1) - * - * After the removal of "FOO" the tree can be compressed as: - * - * "FOOBAR" -> [] (2) - * - * - * Example of case "2". A tree stores the keys "FOOBAR" = 1 and - * "FOOTER" = 2: - * - * |B| -> "AR" -> [] (1) - * "FOO" -> |-| - * |T| -> "ER" -> [] (2) - * - * After the removal of "FOOTER" the resulting tree is: - * - * "FOO" -> |B| -> "AR" -> [] (1) - * - * That can be compressed into: - * - * "FOOBAR" -> [] (1) - */ - if (trycompress) { - debugf("After removing %.*s:\n", (int)len, s); - debugnode("Compression may be needed",h); - debugf("Seek start node\n"); - - /* Try to reach the upper node that is compressible. - * At the end of the loop 'h' will point to the first node we - * can try to compress and 'parent' to its parent. */ - raxNode *parent; - while(1) { - parent = raxStackPop(&ts); - if (!parent || parent->iskey || - (!parent->iscompr && parent->size != 1)) break; - h = parent; - debugnode("Going up to",h); - } - raxNode *start = h; /* Compression starting node. */ - - /* Scan chain of nodes we can compress. */ - size_t comprsize = h->size; - int nodes = 1; - while(h->size != 0) { - raxNode **cp = raxNodeLastChildPtr(h); - memcpy(&h,cp,sizeof(h)); - if (h->iskey || (!h->iscompr && h->size != 1)) break; - /* Stop here if going to the next node would result into - * a compressed node larger than h->size can hold. */ - if (comprsize + h->size > RAX_NODE_MAX_SIZE) break; - nodes++; - comprsize += h->size; - } - if (nodes > 1) { - /* If we can compress, create the new node and populate it. */ - size_t nodesize = - sizeof(raxNode)+comprsize+raxPadding(comprsize)+sizeof(raxNode*); - raxNode *new = rax_malloc(nodesize); - /* An out of memory here just means we cannot optimize this - * node, but the tree is left in a consistent state. */ - if (new == NULL) { - raxStackFree(&ts); - return 1; - } - new->iskey = 0; - new->isnull = 0; - new->iscompr = 1; - new->size = comprsize; - rax->numnodes++; - - /* Scan again, this time to populate the new node content and - * to fix the new node child pointer. At the same time we free - * all the nodes that we'll no longer use. */ - comprsize = 0; - h = start; - while(h->size != 0) { - memcpy(new->data+comprsize,h->data,h->size); - comprsize += h->size; - raxNode **cp = raxNodeLastChildPtr(h); - raxNode *tofree = h; - memcpy(&h,cp,sizeof(h)); - rax_free(tofree); rax->numnodes--; - if (h->iskey || (!h->iscompr && h->size != 1)) break; - } - debugnode("New node",new); - - /* Now 'h' points to the first node that we still need to use, - * so our new node child pointer will point to it. */ - raxNode **cp = raxNodeLastChildPtr(new); - memcpy(cp,&h,sizeof(h)); - - /* Fix parent link. */ - if (parent) { - raxNode **parentlink = raxFindParentLink(parent,start); - memcpy(parentlink,&new,sizeof(new)); - } else { - rax->head = new; - } - - debugf("Compressed %d nodes, %d total bytes\n", - nodes, (int)comprsize); - } - } - raxStackFree(&ts); - return 1; -} - -/* This is the core of raxFree(): performs a depth-first scan of the - * tree and releases all the nodes found. */ -void raxRecursiveFree(rax *rax, raxNode *n, void (*free_callback)(void*)) { - debugnode("free traversing",n); - int numchildren = n->iscompr ? 1 : n->size; - raxNode **cp = raxNodeLastChildPtr(n); - while(numchildren--) { - raxNode *child; - memcpy(&child,cp,sizeof(child)); - raxRecursiveFree(rax,child,free_callback); - cp--; - } - debugnode("free depth-first",n); - if (free_callback && n->iskey && !n->isnull) - free_callback(raxGetData(n)); - rax_free(n); - rax->numnodes--; -} - -/* Free a whole radix tree, calling the specified callback in order to - * free the auxiliary data. */ -void raxFreeWithCallback(rax *rax, void (*free_callback)(void*)) { - raxRecursiveFree(rax,rax->head,free_callback); - assert(rax->numnodes == 0); - rax_free(rax); -} - -/* Free a whole radix tree. */ -void raxFree(rax *rax) { - raxFreeWithCallback(rax,NULL); -} - -/* ------------------------------- Iterator --------------------------------- */ - -/* Initialize a Rax iterator. This call should be performed a single time - * to initialize the iterator, and must be followed by a raxSeek() call, - * otherwise the raxPrev()/raxNext() functions will just return EOF. */ -void raxStart(raxIterator *it, rax *rt) { - it->flags = RAX_ITER_EOF; /* No crash if the iterator is not seeked. */ - it->rt = rt; - it->key_len = 0; - it->key = it->key_static_string; - it->key_max = RAX_ITER_STATIC_LEN; - it->data = NULL; - it->node_cb = NULL; - raxStackInit(&it->stack); -} - -/* Append characters at the current key string of the iterator 'it'. This - * is a low level function used to implement the iterator, not callable by - * the user. Returns 0 on out of memory, otherwise 1 is returned. */ -int raxIteratorAddChars(raxIterator *it, unsigned char *s, size_t len) { - if (len == 0) return 1; - if (it->key_max < it->key_len+len) { - unsigned char *old = (it->key == it->key_static_string) ? NULL : - it->key; - size_t new_max = (it->key_len+len)*2; - it->key = rax_realloc(old,new_max); - if (it->key == NULL) { - it->key = (!old) ? it->key_static_string : old; - errno = ENOMEM; - return 0; - } - if (old == NULL) memcpy(it->key,it->key_static_string,it->key_len); - it->key_max = new_max; - } - /* Use memmove since there could be an overlap between 's' and - * it->key when we use the current key in order to re-seek. */ - memmove(it->key+it->key_len,s,len); - it->key_len += len; - return 1; -} - -/* Remove the specified number of chars from the right of the current - * iterator key. */ -void raxIteratorDelChars(raxIterator *it, size_t count) { - it->key_len -= count; -} - -/* Do an iteration step towards the next element. At the end of the step the - * iterator key will represent the (new) current key. If it is not possible - * to step in the specified direction since there are no longer elements, the - * iterator is flagged with RAX_ITER_EOF. - * - * If 'noup' is true the function starts directly scanning for the next - * lexicographically smaller children, and the current node is already assumed - * to be the parent of the last key node, so the first operation to go back to - * the parent will be skipped. This option is used by raxSeek() when - * implementing seeking a non existing element with the ">" or "<" options: - * the starting node is not a key in that particular case, so we start the scan - * from a node that does not represent the key set. - * - * The function returns 1 on success or 0 on out of memory. */ -int raxIteratorNextStep(raxIterator *it, int noup) { - if (it->flags & RAX_ITER_EOF) { - return 1; - } else if (it->flags & RAX_ITER_JUST_SEEKED) { - it->flags &= ~RAX_ITER_JUST_SEEKED; - return 1; - } - - /* Save key len, stack items and the node where we are currently - * so that on iterator EOF we can restore the current key and state. */ - size_t orig_key_len = it->key_len; - size_t orig_stack_items = it->stack.items; - raxNode *orig_node = it->node; - - while(1) { - int children = it->node->iscompr ? 1 : it->node->size; - if (!noup && children) { - debugf("GO DEEPER\n"); - /* Seek the lexicographically smaller key in this subtree, which - * is the first one found always going towards the first child - * of every successive node. */ - if (!raxStackPush(&it->stack,it->node)) return 0; - raxNode **cp = raxNodeFirstChildPtr(it->node); - if (!raxIteratorAddChars(it,it->node->data, - it->node->iscompr ? it->node->size : 1)) return 0; - memcpy(&it->node,cp,sizeof(it->node)); - /* Call the node callback if any, and replace the node pointer - * if the callback returns true. */ - if (it->node_cb && it->node_cb(&it->node)) - memcpy(cp,&it->node,sizeof(it->node)); - /* For "next" step, stop every time we find a key along the - * way, since the key is lexicographically smaller compared to - * what follows in the sub-children. */ - if (it->node->iskey) { - it->data = raxGetData(it->node); - return 1; - } - } else { - /* If we finished exploring the previous sub-tree, switch to the - * new one: go upper until a node is found where there are - * children representing keys lexicographically greater than the - * current key. */ - while(1) { - int old_noup = noup; - - /* Already on head? Can't go up, iteration finished. */ - if (!noup && it->node == it->rt->head) { - it->flags |= RAX_ITER_EOF; - it->stack.items = orig_stack_items; - it->key_len = orig_key_len; - it->node = orig_node; - return 1; - } - /* If there are no children at the current node, try parent's - * next child. */ - unsigned char prevchild = it->key[it->key_len-1]; - if (!noup) { - it->node = raxStackPop(&it->stack); - } else { - noup = 0; - } - /* Adjust the current key to represent the node we are - * at. */ - int todel = it->node->iscompr ? it->node->size : 1; - raxIteratorDelChars(it,todel); - - /* Try visiting the next child if there was at least one - * additional child. */ - if (!it->node->iscompr && it->node->size > (old_noup ? 0 : 1)) { - raxNode **cp = raxNodeFirstChildPtr(it->node); - int i = 0; - while (i < it->node->size) { - debugf("SCAN NEXT %c\n", it->node->data[i]); - if (it->node->data[i] > prevchild) break; - i++; - cp++; - } - if (i != it->node->size) { - debugf("SCAN found a new node\n"); - raxIteratorAddChars(it,it->node->data+i,1); - if (!raxStackPush(&it->stack,it->node)) return 0; - memcpy(&it->node,cp,sizeof(it->node)); - /* Call the node callback if any, and replace the node - * pointer if the callback returns true. */ - if (it->node_cb && it->node_cb(&it->node)) - memcpy(cp,&it->node,sizeof(it->node)); - if (it->node->iskey) { - it->data = raxGetData(it->node); - return 1; - } - break; - } - } - } - } - } -} - -/* Seek the greatest key in the subtree at the current node. Return 0 on - * out of memory, otherwise 1. This is a helper function for different - * iteration functions below. */ -int raxSeekGreatest(raxIterator *it) { - while(it->node->size) { - if (it->node->iscompr) { - if (!raxIteratorAddChars(it,it->node->data, - it->node->size)) return 0; - } else { - if (!raxIteratorAddChars(it,it->node->data+it->node->size-1,1)) - return 0; - } - raxNode **cp = raxNodeLastChildPtr(it->node); - if (!raxStackPush(&it->stack,it->node)) return 0; - memcpy(&it->node,cp,sizeof(it->node)); - } - return 1; -} - -/* Like raxIteratorNextStep() but implements an iteration step moving - * to the lexicographically previous element. The 'noup' option has a similar - * effect to the one of raxIteratorNextStep(). */ -int raxIteratorPrevStep(raxIterator *it, int noup) { - if (it->flags & RAX_ITER_EOF) { - return 1; - } else if (it->flags & RAX_ITER_JUST_SEEKED) { - it->flags &= ~RAX_ITER_JUST_SEEKED; - return 1; - } - - /* Save key len, stack items and the node where we are currently - * so that on iterator EOF we can restore the current key and state. */ - size_t orig_key_len = it->key_len; - size_t orig_stack_items = it->stack.items; - raxNode *orig_node = it->node; - - while(1) { - int old_noup = noup; - - /* Already on head? Can't go up, iteration finished. */ - if (!noup && it->node == it->rt->head) { - it->flags |= RAX_ITER_EOF; - it->stack.items = orig_stack_items; - it->key_len = orig_key_len; - it->node = orig_node; - return 1; - } - - unsigned char prevchild = it->key[it->key_len-1]; - if (!noup) { - it->node = raxStackPop(&it->stack); - } else { - noup = 0; - } - - /* Adjust the current key to represent the node we are - * at. */ - int todel = it->node->iscompr ? it->node->size : 1; - raxIteratorDelChars(it,todel); - - /* Try visiting the prev child if there is at least one - * child. */ - if (!it->node->iscompr && it->node->size > (old_noup ? 0 : 1)) { - raxNode **cp = raxNodeLastChildPtr(it->node); - int i = it->node->size-1; - while (i >= 0) { - debugf("SCAN PREV %c\n", it->node->data[i]); - if (it->node->data[i] < prevchild) break; - i--; - cp--; - } - /* If we found a new subtree to explore in this node, - * go deeper following all the last children in order to - * find the key lexicographically greater. */ - if (i != -1) { - debugf("SCAN found a new node\n"); - /* Enter the node we just found. */ - if (!raxIteratorAddChars(it,it->node->data+i,1)) return 0; - if (!raxStackPush(&it->stack,it->node)) return 0; - memcpy(&it->node,cp,sizeof(it->node)); - /* Seek sub-tree max. */ - if (!raxSeekGreatest(it)) return 0; - } - } - - /* Return the key: this could be the key we found scanning a new - * subtree, or if we did not find a new subtree to explore here, - * before giving up with this node, check if it's a key itself. */ - if (it->node->iskey) { - it->data = raxGetData(it->node); - return 1; - } - } -} - -/* Seek an iterator at the specified element. - * Return 0 if the seek failed for syntax error or out of memory. Otherwise - * 1 is returned. When 0 is returned for out of memory, errno is set to - * the ENOMEM value. */ -int raxSeek(raxIterator *it, const char *op, unsigned char *ele, size_t len) { - int eq = 0, lt = 0, gt = 0, first = 0, last = 0; - - it->stack.items = 0; /* Just resetting. Initialized by raxStart(). */ - it->flags |= RAX_ITER_JUST_SEEKED; - it->flags &= ~RAX_ITER_EOF; - it->key_len = 0; - it->node = NULL; - - /* Set flags according to the operator used to perform the seek. */ - if (op[0] == '>') { - gt = 1; - if (op[1] == '=') eq = 1; - } else if (op[0] == '<') { - lt = 1; - if (op[1] == '=') eq = 1; - } else if (op[0] == '=') { - eq = 1; - } else if (op[0] == '^') { - first = 1; - } else if (op[0] == '$') { - last = 1; - } else { - errno = 0; - return 0; /* Error. */ - } - - /* If there are no elements, set the EOF condition immediately and - * return. */ - if (it->rt->numele == 0) { - it->flags |= RAX_ITER_EOF; - return 1; - } - - if (first) { - /* Seeking the first key greater or equal to the empty string - * is equivalent to seeking the smaller key available. */ - return raxSeek(it,">=",NULL,0); - } - - if (last) { - /* Find the greatest key taking always the last child till a - * final node is found. */ - it->node = it->rt->head; - if (!raxSeekGreatest(it)) return 0; - assert(it->node->iskey); - it->data = raxGetData(it->node); - return 1; - } - - /* We need to seek the specified key. What we do here is to actually - * perform a lookup, and later invoke the prev/next key code that - * we already use for iteration. */ - int splitpos = 0; - size_t i = raxLowWalk(it->rt,ele,len,&it->node,NULL,&splitpos,&it->stack); - - /* Return OOM on incomplete stack info. */ - if (it->stack.oom) return 0; - - if (eq && i == len && (!it->node->iscompr || splitpos == 0) && - it->node->iskey) - { - /* We found our node, since the key matches and we have an - * "equal" condition. */ - if (!raxIteratorAddChars(it,ele,len)) return 0; /* OOM. */ - it->data = raxGetData(it->node); - } else if (lt || gt) { - /* Exact key not found or eq flag not set. We have to set as current - * key the one represented by the node we stopped at, and perform - * a next/prev operation to seek. */ - raxIteratorAddChars(it, ele, i-splitpos); - - /* We need to set the iterator in the correct state to call next/prev - * step in order to seek the desired element. */ - debugf("After initial seek: i=%d len=%d key=%.*s\n", - (int)i, (int)len, (int)it->key_len, it->key); - if (i != len && !it->node->iscompr) { - /* If we stopped in the middle of a normal node because of a - * mismatch, add the mismatching character to the current key - * and call the iterator with the 'noup' flag so that it will try - * to seek the next/prev child in the current node directly based - * on the mismatching character. */ - if (!raxIteratorAddChars(it,ele+i,1)) return 0; - debugf("Seek normal node on mismatch: %.*s\n", - (int)it->key_len, (char*)it->key); - - it->flags &= ~RAX_ITER_JUST_SEEKED; - if (lt && !raxIteratorPrevStep(it,1)) return 0; - if (gt && !raxIteratorNextStep(it,1)) return 0; - it->flags |= RAX_ITER_JUST_SEEKED; /* Ignore next call. */ - } else if (i != len && it->node->iscompr) { - debugf("Compressed mismatch: %.*s\n", - (int)it->key_len, (char*)it->key); - /* In case of a mismatch within a compressed node. */ - int nodechar = it->node->data[splitpos]; - int keychar = ele[i]; - it->flags &= ~RAX_ITER_JUST_SEEKED; - if (gt) { - /* If the key the compressed node represents is greater - * than our seek element, continue forward, otherwise set the - * state in order to go back to the next sub-tree. */ - if (nodechar > keychar) { - if (!raxIteratorNextStep(it,0)) return 0; - } else { - if (!raxIteratorAddChars(it,it->node->data,it->node->size)) - return 0; - if (!raxIteratorNextStep(it,1)) return 0; - } - } - if (lt) { - /* If the key the compressed node represents is smaller - * than our seek element, seek the greater key in this - * subtree, otherwise set the state in order to go back to - * the previous sub-tree. */ - if (nodechar < keychar) { - if (!raxSeekGreatest(it)) return 0; - it->data = raxGetData(it->node); - } else { - if (!raxIteratorAddChars(it,it->node->data,it->node->size)) - return 0; - if (!raxIteratorPrevStep(it,1)) return 0; - } - } - it->flags |= RAX_ITER_JUST_SEEKED; /* Ignore next call. */ - } else { - debugf("No mismatch: %.*s\n", - (int)it->key_len, (char*)it->key); - /* If there was no mismatch we are into a node representing the - * key, (but which is not a key or the seek operator does not - * include 'eq'), or we stopped in the middle of a compressed node - * after processing all the key. Continue iterating as this was - * a legitimate key we stopped at. */ - it->flags &= ~RAX_ITER_JUST_SEEKED; - if (it->node->iscompr && it->node->iskey && splitpos && lt) { - /* If we stopped in the middle of a compressed node with - * perfect match, and the condition is to seek a key "<" than - * the specified one, then if this node is a key it already - * represents our match. For instance we may have nodes: - * - * "f" -> "oobar" = 1 -> "" = 2 - * - * Representing keys "f" = 1, "foobar" = 2. A seek for - * the key < "foo" will stop in the middle of the "oobar" - * node, but will be our match, representing the key "f". - * - * So in that case, we don't seek backward. */ - it->data = raxGetData(it->node); - } else { - if (gt && !raxIteratorNextStep(it,0)) return 0; - if (lt && !raxIteratorPrevStep(it,0)) return 0; - } - it->flags |= RAX_ITER_JUST_SEEKED; /* Ignore next call. */ - } - } else { - /* If we are here just eq was set but no match was found. */ - it->flags |= RAX_ITER_EOF; - return 1; - } - return 1; -} - -/* Go to the next element in the scope of the iterator 'it'. - * If EOF (or out of memory) is reached, 0 is returned, otherwise 1 is - * returned. In case 0 is returned because of OOM, errno is set to ENOMEM. */ -int raxNext(raxIterator *it) { - if (!raxIteratorNextStep(it,0)) { - errno = ENOMEM; - return 0; - } - if (it->flags & RAX_ITER_EOF) { - errno = 0; - return 0; - } - return 1; -} - -/* Go to the previous element in the scope of the iterator 'it'. - * If EOF (or out of memory) is reached, 0 is returned, otherwise 1 is - * returned. In case 0 is returned because of OOM, errno is set to ENOMEM. */ -int raxPrev(raxIterator *it) { - if (!raxIteratorPrevStep(it,0)) { - errno = ENOMEM; - return 0; - } - if (it->flags & RAX_ITER_EOF) { - errno = 0; - return 0; - } - return 1; -} - -/* Perform a random walk starting in the current position of the iterator. - * Return 0 if the tree is empty or on out of memory. Otherwise 1 is returned - * and the iterator is set to the node reached after doing a random walk - * of 'steps' steps. If the 'steps' argument is 0, the random walk is performed - * using a random number of steps between 1 and two times the logarithm of - * the number of elements. - * - * NOTE: if you use this function to generate random elements from the radix - * tree, expect a disappointing distribution. A random walk produces good - * random elements if the tree is not sparse, however in the case of a radix - * tree certain keys will be reported much more often than others. At least - * this function should be able to explore every possible element eventually. */ -int raxRandomWalk(raxIterator *it, size_t steps) { - if (it->rt->numele == 0) { - it->flags |= RAX_ITER_EOF; - return 0; - } - - if (steps == 0) { - size_t fle = 1+floor(log(it->rt->numele)); - fle *= 2; - steps = 1 + rand() % fle; - } - - raxNode *n = it->node; - while(steps > 0 || !n->iskey) { - int numchildren = n->iscompr ? 1 : n->size; - int r = rand() % (numchildren+(n != it->rt->head)); - - if (r == numchildren) { - /* Go up to parent. */ - n = raxStackPop(&it->stack); - int todel = n->iscompr ? n->size : 1; - raxIteratorDelChars(it,todel); - } else { - /* Select a random child. */ - if (n->iscompr) { - if (!raxIteratorAddChars(it,n->data,n->size)) return 0; - } else { - if (!raxIteratorAddChars(it,n->data+r,1)) return 0; - } - raxNode **cp = raxNodeFirstChildPtr(n)+r; - if (!raxStackPush(&it->stack,n)) return 0; - memcpy(&n,cp,sizeof(n)); - } - if (n->iskey) steps--; - } - it->node = n; - it->data = raxGetData(it->node); - return 1; -} - -/* Compare the key currently pointed by the iterator to the specified - * key according to the specified operator. Returns 1 if the comparison is - * true, otherwise 0 is returned. */ -int raxCompare(raxIterator *iter, const char *op, unsigned char *key, size_t key_len) { - int eq = 0, lt = 0, gt = 0; - - if (op[0] == '=' || op[1] == '=') eq = 1; - if (op[0] == '>') gt = 1; - else if (op[0] == '<') lt = 1; - else if (op[1] != '=') return 0; /* Syntax error. */ - - size_t minlen = key_len < iter->key_len ? key_len : iter->key_len; - int cmp = memcmp(iter->key,key,minlen); - - /* Handle == */ - if (lt == 0 && gt == 0) return cmp == 0 && key_len == iter->key_len; - - /* Handle >, >=, <, <= */ - if (cmp == 0) { - /* Same prefix: longer wins. */ - if (eq && key_len == iter->key_len) return 1; - else if (lt) return iter->key_len < key_len; - else if (gt) return iter->key_len > key_len; - else return 0; /* Avoid warning, just 'eq' is handled before. */ - } else if (cmp > 0) { - return gt ? 1 : 0; - } else /* (cmp < 0) */ { - return lt ? 1 : 0; - } -} - -/* Free the iterator. */ -void raxStop(raxIterator *it) { - if (it->key != it->key_static_string) rax_free(it->key); - raxStackFree(&it->stack); -} - -/* Return if the iterator is in an EOF state. This happens when raxSeek() - * failed to seek an appropriate element, so that raxNext() or raxPrev() - * will return zero, or when an EOF condition was reached while iterating - * with raxNext() and raxPrev(). */ -int raxEOF(raxIterator *it) { - return it->flags & RAX_ITER_EOF; -} - -/* Return the number of elements inside the radix tree. */ -uint64_t raxSize(rax *rax) { - return rax->numele; -} - -/* ----------------------------- Introspection ------------------------------ */ - -/* This function is mostly used for debugging and learning purposes. - * It shows an ASCII representation of a tree on standard output, outline - * all the nodes and the contained keys. - * - * The representation is as follow: - * - * "foobar" (compressed node) - * [abc] (normal node with three children) - * [abc]=0x12345678 (node is a key, pointing to value 0x12345678) - * [] (a normal empty node) - * - * Children are represented in new indented lines, each children prefixed by - * the "`-(x)" string, where "x" is the edge byte. - * - * [abc] - * `-(a) "ladin" - * `-(b) [kj] - * `-(c) [] - * - * However when a node has a single child the following representation - * is used instead: - * - * [abc] -> "ladin" -> [] - */ - -/* The actual implementation of raxShow(). */ -void raxRecursiveShow(int level, int lpad, raxNode *n) { - char s = n->iscompr ? '"' : '['; - char e = n->iscompr ? '"' : ']'; - - int numchars = printf("%c%.*s%c", s, n->size, n->data, e); - if (n->iskey) { - numchars += printf("=%p",raxGetData(n)); - } - - int numchildren = n->iscompr ? 1 : n->size; - /* Note that 7 and 4 magic constants are the string length - * of " `-(x) " and " -> " respectively. */ - if (level) { - lpad += (numchildren > 1) ? 7 : 4; - if (numchildren == 1) lpad += numchars; - } - raxNode **cp = raxNodeFirstChildPtr(n); - for (int i = 0; i < numchildren; i++) { - char *branch = " `-(%c) "; - if (numchildren > 1) { - printf("\n"); - for (int j = 0; j < lpad; j++) putchar(' '); - printf(branch,n->data[i]); - } else { - printf(" -> "); - } - raxNode *child; - memcpy(&child,cp,sizeof(child)); - raxRecursiveShow(level+1,lpad,child); - cp++; - } -} - -/* Show a tree, as outlined in the comment above. */ -void raxShow(rax *rax) { - raxRecursiveShow(0,0,rax->head); - putchar('\n'); -} - -/* Used by debugnode() macro to show info about a given node. */ -void raxDebugShowNode(const char *msg, raxNode *n) { - if (raxDebugMsg == 0) return; - printf("%s: %p [%.*s] key:%u size:%u children:", - msg, (void*)n, (int)n->size, (char*)n->data, n->iskey, n->size); - int numcld = n->iscompr ? 1 : n->size; - raxNode **cldptr = raxNodeLastChildPtr(n) - (numcld-1); - while(numcld--) { - raxNode *child; - memcpy(&child,cldptr,sizeof(child)); - cldptr++; - printf("%p ", (void*)child); - } - printf("\n"); - fflush(stdout); -} - -/* Touch all the nodes of a tree returning a check sum. This is useful - * in order to make Valgrind detect if there is something wrong while - * reading the data structure. - * - * This function was used in order to identify Rax bugs after a big refactoring - * using this technique: - * - * 1. The rax-test is executed using Valgrind, adding a printf() so that for - * the fuzz tester we see what iteration in the loop we are in. - * 2. After every modification of the radix tree made by the fuzz tester - * in rax-test.c, we add a call to raxTouch(). - * 3. Now as soon as an operation will corrupt the tree, raxTouch() will - * detect it (via Valgrind) immediately. We can add more calls to narrow - * the state. - * 4. At this point a good idea is to enable Rax debugging messages immediately - * before the moment the tree is corrupted, to see what happens. - */ -unsigned long raxTouch(raxNode *n) { - debugf("Touching %p\n", (void*)n); - unsigned long sum = 0; - if (n->iskey) { - sum += (unsigned long)raxGetData(n); - } - - int numchildren = n->iscompr ? 1 : n->size; - raxNode **cp = raxNodeFirstChildPtr(n); - int count = 0; - for (int i = 0; i < numchildren; i++) { - if (numchildren > 1) { - sum += (long)n->data[i]; - } - raxNode *child; - memcpy(&child,cp,sizeof(child)); - if (child == (void*)0x65d1760) count++; - if (count > 1) exit(1); - sum += raxTouch(child); - cp++; - } - return sum; -} diff --git a/src/libsysprof/rax.h b/src/libsysprof/rax.h deleted file mode 100644 index 6b1fd418..00000000 --- a/src/libsysprof/rax.h +++ /dev/null @@ -1,216 +0,0 @@ -/* Rax -- A radix tree implementation. - * - * Copyright (c) 2017-2018, Salvatore Sanfilippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef RAX_H -#define RAX_H - -#include - -/* Representation of a radix tree as implemented in this file, that contains - * the strings "foo", "foobar" and "footer" after the insertion of each - * word. When the node represents a key inside the radix tree, we write it - * between [], otherwise it is written between (). - * - * This is the vanilla representation: - * - * (f) "" - * \ - * (o) "f" - * \ - * (o) "fo" - * \ - * [t b] "foo" - * / \ - * "foot" (e) (a) "foob" - * / \ - * "foote" (r) (r) "fooba" - * / \ - * "footer" [] [] "foobar" - * - * However, this implementation implements a very common optimization where - * successive nodes having a single child are "compressed" into the node - * itself as a string of characters, each representing a next-level child, - * and only the link to the node representing the last character node is - * provided inside the representation. So the above representation is turned - * into: - * - * ["foo"] "" - * | - * [t b] "foo" - * / \ - * "foot" ("er") ("ar") "foob" - * / \ - * "footer" [] [] "foobar" - * - * However this optimization makes the implementation a bit more complex. - * For instance if a key "first" is added in the above radix tree, a - * "node splitting" operation is needed, since the "foo" prefix is no longer - * composed of nodes having a single child one after the other. This is the - * above tree and the resulting node splitting after this event happens: - * - * - * (f) "" - * / - * (i o) "f" - * / \ - * "firs" ("rst") (o) "fo" - * / \ - * "first" [] [t b] "foo" - * / \ - * "foot" ("er") ("ar") "foob" - * / \ - * "footer" [] [] "foobar" - * - * Similarly after deletion, if a new chain of nodes having a single child - * is created (the chain must also not include nodes that represent keys), - * it must be compressed back into a single node. - * - */ - -#define RAX_NODE_MAX_SIZE ((1<<29)-1) -typedef struct raxNode { - uint32_t iskey:1; /* Does this node contain a key? */ - uint32_t isnull:1; /* Associated value is NULL (don't store it). */ - uint32_t iscompr:1; /* Node is compressed. */ - uint32_t size:29; /* Number of children, or compressed string len. */ - /* Data layout is as follows: - * - * If node is not compressed we have 'size' bytes, one for each children - * character, and 'size' raxNode pointers, point to each child node. - * Note how the character is not stored in the children but in the - * edge of the parents: - * - * [header iscompr=0][abc][a-ptr][b-ptr][c-ptr](value-ptr?) - * - * if node is compressed (iscompr bit is 1) the node has 1 children. - * In that case the 'size' bytes of the string stored immediately at - * the start of the data section, represent a sequence of successive - * nodes linked one after the other, for which only the last one in - * the sequence is actually represented as a node, and pointed to by - * the current compressed node. - * - * [header iscompr=1][xyz][z-ptr](value-ptr?) - * - * Both compressed and not compressed nodes can represent a key - * with associated data in the radix tree at any level (not just terminal - * nodes). - * - * If the node has an associated key (iskey=1) and is not NULL - * (isnull=0), then after the raxNode pointers pointing to the - * children, an additional value pointer is present (as you can see - * in the representation above as "value-ptr" field). - */ - unsigned char data[]; -} raxNode; - -typedef struct rax { - raxNode *head; - uint64_t numele; - uint64_t numnodes; -} rax; - -/* Stack data structure used by raxLowWalk() in order to, optionally, return - * a list of parent nodes to the caller. The nodes do not have a "parent" - * field for space concerns, so we use the auxiliary stack when needed. */ -#define RAX_STACK_STATIC_ITEMS 32 -typedef struct raxStack { - void **stack; /* Points to static_items or an heap allocated array. */ - size_t items, maxitems; /* Number of items contained and total space. */ - /* Up to RAXSTACK_STACK_ITEMS items we avoid to allocate on the heap - * and use this static array of pointers instead. */ - void *static_items[RAX_STACK_STATIC_ITEMS]; - int oom; /* True if pushing into this stack failed for OOM at some point. */ -} raxStack; - -/* Optional callback used for iterators and be notified on each rax node, - * including nodes not representing keys. If the callback returns true - * the callback changed the node pointer in the iterator structure, and the - * iterator implementation will have to replace the pointer in the radix tree - * internals. This allows the callback to reallocate the node to perform - * very special operations, normally not needed by normal applications. - * - * This callback is used to perform very low level analysis of the radix tree - * structure, scanning each possible node (but the root node), or in order to - * reallocate the nodes to reduce the allocation fragmentation (this is the - * Redis application for this callback). - * - * This is currently only supported in forward iterations (raxNext) */ -typedef int (*raxNodeCallback)(raxNode **noderef); - -/* Radix tree iterator state is encapsulated into this data structure. */ -#define RAX_ITER_STATIC_LEN 128 -#define RAX_ITER_JUST_SEEKED (1<<0) /* Iterator was just seeked. Return current - element for the first iteration and - clear the flag. */ -#define RAX_ITER_EOF (1<<1) /* End of iteration reached. */ -#define RAX_ITER_SAFE (1<<2) /* Safe iterator, allows operations while - iterating. But it is slower. */ -typedef struct raxIterator { - int flags; - rax *rt; /* Radix tree we are iterating. */ - unsigned char *key; /* The current string. */ - void *data; /* Data associated to this key. */ - size_t key_len; /* Current key length. */ - size_t key_max; /* Max key len the current key buffer can hold. */ - unsigned char key_static_string[RAX_ITER_STATIC_LEN]; - raxNode *node; /* Current node. Only for unsafe iteration. */ - raxStack stack; /* Stack used for unsafe iteration. */ - raxNodeCallback node_cb; /* Optional node callback. Normally set to NULL. */ -} raxIterator; - -/* A special pointer returned for not found items. */ -extern void *raxNotFound; - -/* Exported API. */ -rax *raxNew(void); -int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old); -int raxTryInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old); -int raxRemove(rax *rax, unsigned char *s, size_t len, void **old); -void *raxFind(rax *rax, unsigned char *s, size_t len); -void raxFree(rax *rax); -void raxFreeWithCallback(rax *rax, void (*free_callback)(void*)); -void raxStart(raxIterator *it, rax *rt); -int raxSeek(raxIterator *it, const char *op, unsigned char *ele, size_t len); -int raxNext(raxIterator *it); -int raxPrev(raxIterator *it); -int raxRandomWalk(raxIterator *it, size_t steps); -int raxCompare(raxIterator *iter, const char *op, unsigned char *key, size_t key_len); -void raxStop(raxIterator *it); -int raxEOF(raxIterator *it); -void raxShow(rax *rax); -uint64_t raxSize(rax *rax); -unsigned long raxTouch(raxNode *n); -void raxSetDebugMsg(int onoff); - -/* Internal API. May be used by the node callback in order to access rax nodes - * in a low level way, so this function is exported as well. */ -void raxSetData(raxNode *n, void *data); - -#endif diff --git a/src/libsysprof/rax_malloc.h b/src/libsysprof/rax_malloc.h deleted file mode 100644 index e9d5d5d7..00000000 --- a/src/libsysprof/rax_malloc.h +++ /dev/null @@ -1,43 +0,0 @@ -/* Rax -- A radix tree implementation. - * - * Copyright (c) 2017, Salvatore Sanfilippo - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Redis nor the names of its contributors may be used - * to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/* Allocator selection. - * - * This file is used in order to change the Rax allocator at compile time. - * Just define the following defines to what you want to use. Also add - * the include of your alternate allocator if needed (not needed in order - * to use the default libc allocator). */ - -#ifndef RAX_ALLOC_H -#define RAX_ALLOC_H -#define rax_malloc malloc -#define rax_realloc realloc -#define rax_free free -#endif diff --git a/src/libsysprof/sysprof-backport-autocleanups.h b/src/libsysprof/sysprof-backport-autocleanups.h deleted file mode 100644 index 381b2735..00000000 --- a/src/libsysprof/sysprof-backport-autocleanups.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "config.h" - -#include - -#if HAVE_POLKIT -# ifndef HAVE_POLKIT_AUTOPTR -# include - - G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAuthority, g_object_unref) - G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAuthorizationResult, g_object_unref) - G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitSubject, g_object_unref) - G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitDetails, g_object_unref) -# endif -#endif - -#if !GLIB_CHECK_VERSION(2, 56, 0) -# define g_clear_handle_id(ptr, clear_func) \ - G_STMT_START { \ - guint __ptr = *(ptr); \ - *(ptr) = 0; \ - if (__ptr != 0) \ - clear_func (__ptr); \ - } G_STMT_END -#endif diff --git a/src/libsysprof/sysprof-battery-source.c b/src/libsysprof/sysprof-battery-source.c deleted file mode 100644 index 7081357f..00000000 --- a/src/libsysprof/sysprof-battery-source.c +++ /dev/null @@ -1,343 +0,0 @@ -/* sysprof-battery-source.c - * - * Copyright 2019 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 G_LOG_DOMAIN "sysprof-battery-source" - -#include "config.h" - -#include -#include -#include - -#include "sysprof-backport-autocleanups.h" -#include "sysprof-battery-source.h" -#include "sysprof-helpers.h" - -struct _SysprofBatterySource -{ - GObject parent_instance; - - SysprofCaptureWriter *writer; - GArray *batteries; - - guint combined_id; - guint poll_source; -}; - -typedef struct -{ - gchar id[32]; - gchar name[52]; - guint charge_full; - guint charge_now; - gint charge_now_fd; - guint counter_id; -} Battery; - -static void source_iface_init (SysprofSourceInterface *); - -G_DEFINE_TYPE_WITH_CODE (SysprofBatterySource, sysprof_battery_source, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init)) - -static void -battery_clear (gpointer data) -{ - Battery *b = data; - - if (b->charge_now_fd != -1) - close (b->charge_now_fd); -} - -static gboolean -sysprof_battery_source_get_is_ready (SysprofSource *source) -{ - return TRUE; -} - -static void -sysprof_battery_source_prepare (SysprofSource *source) -{ - SysprofBatterySource *self = (SysprofBatterySource *)source; - g_autoptr(GDir) dir = NULL; - g_autoptr(GArray) counters = NULL; - const gchar *name; - - g_assert (SYSPROF_IS_BATTERY_SOURCE (self)); - -#define BAT_BASE_PATH "/sys/class/power_supply/" - - counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter)); - - if (!(dir = g_dir_open (BAT_BASE_PATH, 0, NULL))) - goto emit_ready; - - while ((name = g_dir_read_name (dir))) - { - g_autofree gchar *type_path = g_strdup_printf (BAT_BASE_PATH "%s/type", name); - g_autofree gchar *model_path = g_strdup_printf (BAT_BASE_PATH "%s/model_name", name); - g_autofree gchar *charge_now_path = g_strdup_printf (BAT_BASE_PATH "%s/charge_now", name); - g_autofree gchar *charge_full_path = g_strdup_printf (BAT_BASE_PATH "%s/charge_full", name); - g_autofree gchar *type_data = NULL; - g_autofree gchar *model_data = NULL; - g_autofree gchar *charge_full_data = NULL; - SysprofCaptureCounter ctr; - Battery bat = {{0}}; - - /* We dn't care about AC */ - if (g_strcmp0 (name, "AC") == 0) - continue; - - if (!g_file_get_contents (type_path, &type_data, NULL, NULL) || - !g_str_has_prefix (type_data, "Battery")) - continue; - - g_strlcpy (bat.id, name, sizeof bat.id); - - if (g_file_get_contents (model_path, &model_data, NULL, NULL)) - g_strlcpy (bat.name, model_data, sizeof bat.name); - - if (g_file_get_contents (charge_full_path, &charge_full_data, NULL, NULL)) - bat.charge_full = atoi (charge_full_data); - - /* Wait for first polling */ - bat.charge_now = 0; - - g_strstrip (bat.id); - g_strstrip (bat.name); - - bat.charge_now_fd = open (charge_now_path, O_RDONLY); - - if (bat.charge_now_fd == -1) - continue; - - bat.counter_id = sysprof_capture_writer_request_counter (self->writer, 1); - - g_strlcpy (ctr.category, "Battery Charge", sizeof ctr.category); - g_strlcpy (ctr.name, bat.id, sizeof ctr.name); - g_snprintf (ctr.description, sizeof ctr.description, "%s (µAh)", bat.name); - ctr.id = bat.counter_id; - ctr.type = SYSPROF_CAPTURE_COUNTER_INT64; - - g_array_append_val (self->batteries, bat); - g_array_append_val (counters, ctr); - } - - if (counters->len > 0) - { - SysprofCaptureCounter ctr = {{0}}; - - self->combined_id = sysprof_capture_writer_request_counter (self->writer, 1); - - /* Add combined counter */ - g_strlcpy (ctr.category, "Battery Charge", sizeof ctr.category); - g_strlcpy (ctr.name, "Combined", sizeof ctr.name); - g_snprintf (ctr.description, sizeof ctr.description, "Combined Battery Charge (µAh)"); - ctr.id = self->combined_id; - ctr.type = SYSPROF_CAPTURE_COUNTER_INT64; - - g_array_append_val (counters, ctr); - - sysprof_capture_writer_define_counters (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - -1, - (gpointer)counters->data, - counters->len); - } - -#undef BAT_BASE_PATH - -emit_ready: - sysprof_source_emit_ready (source); -} - -static gboolean -battery_poll (Battery *battery, - SysprofCaptureCounterValue *value) -{ - gint64 val; - gssize len; - gchar buf[32]; - - g_assert (battery != NULL); - - if (battery->charge_now_fd == -1) - return FALSE; - - if (lseek (battery->charge_now_fd, 0, SEEK_SET) != 0) - { - close (battery->charge_now_fd); - battery->charge_now_fd = -1; - return FALSE; - } - - len = read (battery->charge_now_fd, buf, sizeof buf - 1); - - if (len < 0) - { - close (battery->charge_now_fd); - battery->charge_now_fd = -1; - return FALSE; - } - - buf [len] = 0; - - val = atoi (buf); - - if (val != battery->charge_now) - { - battery->charge_now = val; - value->v64 = val; - return TRUE; - } - - return FALSE; -} - -static gboolean -sysprof_battery_source_poll_cb (gpointer data) -{ - SysprofBatterySource *self = data; - g_autoptr(GArray) values = NULL; - g_autoptr(GArray) ids = NULL; - gint64 combined = 0; - - g_assert (SYSPROF_IS_BATTERY_SOURCE (self)); - - values = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounterValue)); - ids = g_array_new (FALSE, FALSE, sizeof (guint)); - - for (guint i = 0; i < self->batteries->len; i++) - { - Battery *battery = &g_array_index (self->batteries, Battery, i); - SysprofCaptureCounterValue value; - - if G_LIKELY (battery_poll (battery, &value)) - { - combined += value.v64; - g_array_append_val (ids, battery->counter_id); - g_array_append_val (values, value); - } - } - - if (values->len > 0) - { - if (self->combined_id != 0) - { - SysprofCaptureCounterValue value = { .v64 = combined, }; - - g_array_append_val (ids, self->combined_id); - g_array_append_val (values, value); - } - - sysprof_capture_writer_set_counters (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - -1, - (gconstpointer)ids->data, - (gconstpointer)values->data, - ids->len); - } - - return G_SOURCE_CONTINUE; -} - -static void -sysprof_battery_source_start (SysprofSource *source) -{ - SysprofBatterySource *self = (SysprofBatterySource *)source; - - g_assert (SYSPROF_IS_BATTERY_SOURCE (self)); - - self->poll_source = g_timeout_add_seconds (1, sysprof_battery_source_poll_cb, self); - - /* Poll immediately */ - sysprof_battery_source_poll_cb (self); -} - -static void -sysprof_battery_source_stop (SysprofSource *source) -{ - SysprofBatterySource *self = (SysprofBatterySource *)source; - - g_assert (SYSPROF_IS_BATTERY_SOURCE (self)); - - /* Poll one last time */ - sysprof_battery_source_poll_cb (self); - - g_clear_handle_id (&self->poll_source, g_source_remove); - - sysprof_source_emit_finished (source); -} - -static void -sysprof_battery_source_set_writer (SysprofSource *source, - SysprofCaptureWriter *writer) -{ - SysprofBatterySource *self = (SysprofBatterySource *)source; - - g_assert (SYSPROF_IS_BATTERY_SOURCE (self)); - g_assert (writer != NULL); - - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); - self->writer = sysprof_capture_writer_ref (writer); -} - -static void -source_iface_init (SysprofSourceInterface *iface) -{ - iface->get_is_ready = sysprof_battery_source_get_is_ready; - iface->prepare = sysprof_battery_source_prepare; - iface->set_writer = sysprof_battery_source_set_writer; - iface->start = sysprof_battery_source_start; - iface->stop = sysprof_battery_source_stop; -} - -static void -sysprof_battery_source_finalize (GObject *object) -{ - SysprofBatterySource *self = (SysprofBatterySource *)object; - - g_clear_pointer (&self->batteries, g_array_unref); - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); - - G_OBJECT_CLASS (sysprof_battery_source_parent_class)->finalize (object); -} - -static void -sysprof_battery_source_class_init (SysprofBatterySourceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_battery_source_finalize; -} - -static void -sysprof_battery_source_init (SysprofBatterySource *self) -{ - self->batteries = g_array_new (FALSE, FALSE, sizeof (Battery)); - g_array_set_clear_func (self->batteries, battery_clear); -} - -SysprofSource * -sysprof_battery_source_new (void) -{ - return g_object_new (SYSPROF_TYPE_BATTERY_SOURCE, NULL); -} diff --git a/src/libsysprof/sysprof-battery-source.h b/src/libsysprof/sysprof-battery-source.h deleted file mode 100644 index 468169f6..00000000 --- a/src/libsysprof/sysprof-battery-source.h +++ /dev/null @@ -1,35 +0,0 @@ -/* sysprof-battery-source.h - * - * Copyright 2019 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 - */ - -#pragma once - -#include "sysprof-source.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_BATTERY_SOURCE (sysprof_battery_source_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofBatterySource, sysprof_battery_source, SYSPROF, BATTERY_SOURCE, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSource *sysprof_battery_source_new (void); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-callgraph-profile.c b/src/libsysprof/sysprof-callgraph-profile.c deleted file mode 100644 index 2991202b..00000000 --- a/src/libsysprof/sysprof-callgraph-profile.c +++ /dev/null @@ -1,551 +0,0 @@ -/* sysprof-callgraph-profile.c - * - * Copyright 2016-2019 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 - */ - -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2009-2012 Soeren Sandmann and others - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include -#include -#include - -#include "../stackstash.h" - -#include "sysprof-capture-util-private.h" - -#include "sysprof-callgraph-profile.h" -#include "sysprof-capture-reader.h" -#include "sysprof-capture-symbol-resolver.h" -#include "sysprof-elf-symbol-resolver.h" -#include "sysprof-jitmap-symbol-resolver.h" -#include "sysprof-kernel-symbol-resolver.h" -#include "sysprof-map-lookaside.h" -#include "sysprof-selection.h" - -#define CHECK_CANCELLABLE_INTERVAL 100 - -struct _SysprofCallgraphProfile -{ - GObject parent_instance; - - SysprofCaptureReader *reader; - SysprofSelection *selection; - StackStash *stash; - GStringChunk *symbols; - GHashTable *tags; -}; - -typedef struct -{ - SysprofCaptureReader *reader; - SysprofSelection *selection; -} Generate; - -static void profile_iface_init (SysprofProfileInterface *iface); - -G_DEFINE_TYPE_EXTENDED (SysprofCallgraphProfile, sysprof_callgraph_profile, G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_PROFILE, profile_iface_init)) - -enum { - PROP_0, - PROP_SELECTION, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -SysprofProfile * -sysprof_callgraph_profile_new (void) -{ - return g_object_new (SYSPROF_TYPE_CALLGRAPH_PROFILE, NULL); -} - -SysprofProfile * -sysprof_callgraph_profile_new_with_selection (SysprofSelection *selection) -{ - return g_object_new (SYSPROF_TYPE_CALLGRAPH_PROFILE, - "selection", selection, - NULL); -} - -static void -sysprof_callgraph_profile_finalize (GObject *object) -{ - SysprofCallgraphProfile *self = (SysprofCallgraphProfile *)object; - - g_clear_pointer (&self->symbols, g_string_chunk_free); - g_clear_pointer (&self->stash, stack_stash_unref); - g_clear_pointer (&self->reader, sysprof_capture_reader_unref); - g_clear_pointer (&self->tags, g_hash_table_unref); - g_clear_object (&self->selection); - - G_OBJECT_CLASS (sysprof_callgraph_profile_parent_class)->finalize (object); -} - -static void -sysprof_callgraph_profile_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofCallgraphProfile *self = SYSPROF_CALLGRAPH_PROFILE (object); - - switch (prop_id) - { - case PROP_SELECTION: - g_value_set_object (value, self->selection); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_callgraph_profile_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofCallgraphProfile *self = SYSPROF_CALLGRAPH_PROFILE (object); - - switch (prop_id) - { - case PROP_SELECTION: - self->selection = g_value_dup_object (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_callgraph_profile_class_init (SysprofCallgraphProfileClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_callgraph_profile_finalize; - object_class->get_property = sysprof_callgraph_profile_get_property; - object_class->set_property = sysprof_callgraph_profile_set_property; - - properties [PROP_SELECTION] = - g_param_spec_object ("selection", - "Selection", - "The selection for filtering the callgraph", - SYSPROF_TYPE_SELECTION, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_callgraph_profile_init (SysprofCallgraphProfile *self) -{ - self->symbols = g_string_chunk_new (_sysprof_getpagesize ()); - self->tags = g_hash_table_new (g_str_hash, g_str_equal); -} - -static void -sysprof_callgraph_profile_set_reader (SysprofProfile *profile, - SysprofCaptureReader *reader) -{ - SysprofCallgraphProfile *self = (SysprofCallgraphProfile *)profile; - - g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (self)); - g_assert (reader != NULL); - - g_clear_pointer (&self->reader, sysprof_capture_reader_unref); - self->reader = sysprof_capture_reader_ref (reader); -} - -static const gchar * -sysprof_callgraph_profile_intern_string_take (SysprofCallgraphProfile *self, - gchar *str) -{ - const gchar *ret; - - g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (self)); - g_assert (str != NULL); - - ret = g_string_chunk_insert_const (self->symbols, str); - g_free (str); - return ret; -} - -static const gchar * -sysprof_callgraph_profile_intern_string (SysprofCallgraphProfile *self, - const gchar *str) -{ - g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (self)); - g_assert (str != NULL); - - return g_string_chunk_insert_const (self->symbols, str); -} - -static void -sysprof_callgraph_profile_generate_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - SysprofCallgraphProfile *self = source_object; - Generate *gen = task_data; - SysprofCaptureReader *reader; - SysprofSelection *selection; - g_autoptr(GArray) resolved = NULL; - g_autoptr(GHashTable) maps_by_pid = NULL; - g_autoptr(GHashTable) cmdlines = NULL; - g_autoptr(GPtrArray) resolvers = NULL; - SysprofCaptureFrameType type; - StackStash *stash = NULL; - StackStash *resolved_stash = NULL; - guint count = 0; - gboolean ret = FALSE; - - g_assert (G_IS_TASK (task)); - g_assert (gen != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - reader = gen->reader; - selection = gen->selection; - - maps_by_pid = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)sysprof_map_lookaside_free); - cmdlines = g_hash_table_new (NULL, NULL); - - stash = stack_stash_new (NULL); - resolved_stash = stack_stash_new (NULL); - - resolvers = g_ptr_array_new_with_free_func (g_object_unref); - g_ptr_array_add (resolvers, sysprof_capture_symbol_resolver_new ()); - g_ptr_array_add (resolvers, sysprof_kernel_symbol_resolver_new ()); - g_ptr_array_add (resolvers, sysprof_elf_symbol_resolver_new ()); - g_ptr_array_add (resolvers, sysprof_jitmap_symbol_resolver_new ()); - - for (guint j = 0; j < resolvers->len; j++) - { - SysprofSymbolResolver *resolver = g_ptr_array_index (resolvers, j); - - sysprof_capture_reader_reset (reader); - sysprof_symbol_resolver_load (resolver, reader); - } - - sysprof_capture_reader_reset (reader); - - /* - * The resolved pointer array is where we stash the names for the - * instruction pointers to pass to the stash stack. All the strings - * need to be deduplicated so that pointer comparison works as if we - * did instruction-pointer comparison. - */ - resolved = g_array_new (FALSE, TRUE, sizeof (guint64)); - - while (sysprof_capture_reader_peek_type (reader, &type)) - { - const SysprofCaptureProcess *pr; - - if (type != SYSPROF_CAPTURE_FRAME_PROCESS) - { - if (!sysprof_capture_reader_skip (reader)) - goto failure; - continue; - } - - if (NULL == (pr = sysprof_capture_reader_read_process (reader))) - goto failure; - - if (!g_hash_table_contains (cmdlines, GINT_TO_POINTER (pr->frame.pid))) - { - g_autofree gchar *cmdline = g_strdup_printf ("[%s]", pr->cmdline); - g_hash_table_insert (cmdlines, - GINT_TO_POINTER (pr->frame.pid), - (gchar *)sysprof_callgraph_profile_intern_string (self, cmdline)); - } - } - - if (g_task_return_error_if_cancelled (task)) - goto cleanup; - - sysprof_capture_reader_reset (reader); - - /* - * Walk through all of the sample events and resolve instruction-pointers - * to symbol names by loading the particular map and extracting the symbol - * name. If we wanted to support dynamic systems, we'd want to extend this - * to parse information from captured data about the languages jit'd code. - */ - while (sysprof_capture_reader_peek_type (reader, &type)) - { - SysprofAddressContext last_context = SYSPROF_ADDRESS_CONTEXT_NONE; - const SysprofCaptureSample *sample; - StackNode *node; - StackNode *iter; - const gchar *cmdline; - guint len = 5; - - if (type != SYSPROF_CAPTURE_FRAME_SAMPLE) - { - if (!sysprof_capture_reader_skip (reader)) - goto failure; - continue; - } - - if (++count == CHECK_CANCELLABLE_INTERVAL) - { - if (g_task_return_error_if_cancelled (task)) - goto cleanup; - } - - if (NULL == (sample = sysprof_capture_reader_read_sample (reader))) - goto failure; - - if (!sysprof_selection_contains (selection, sample->frame.time)) - continue; - - if (sample->n_addrs == 0) - continue; - - cmdline = g_hash_table_lookup (cmdlines, GINT_TO_POINTER (sample->frame.pid)); - - if (cmdline == NULL) - { - gchar *pidstr = g_strdup_printf ("[Process %d]", sample->frame.pid); - g_hash_table_insert (cmdlines, GINT_TO_POINTER (sample->frame.pid), pidstr); - cmdline = pidstr; - } - -#if 0 - /* This assertion appears to hold true, but since we're taking in - * untrusted data from capture files, it's not safe to assume. But in - * practice it is. - */ - g_assert (sysprof_address_is_context_switch (sample->addrs[0], &last_context)); - last_context = SYSPROF_ADDRESS_CONTEXT_NONE; -#endif - - node = stack_stash_add_trace (stash, (gpointer)sample->addrs, sample->n_addrs, 1); - - for (iter = node; iter != NULL; iter = iter->parent) - len++; - - if (G_UNLIKELY (resolved->len < len)) - g_array_set_size (resolved, len); - - len = 0; - - for (iter = node; iter != NULL; iter = iter->parent) - { - SysprofAddressContext context = SYSPROF_ADDRESS_CONTEXT_NONE; - SysprofAddress address = iter->data; - const gchar *symbol = NULL; - - if (sysprof_address_is_context_switch (address, &context)) - { - if (last_context) - symbol = sysprof_address_context_to_string (last_context); - else - symbol = NULL; - - last_context = context; - } - else - { - /* In case we get plain backtraces that aren't coming from perf, - * we might never get a context switch into user-space. This ensures - * that we still get traces for things from backtrace(). - */ - if (last_context == SYSPROF_ADDRESS_CONTEXT_NONE) - last_context = SYSPROF_ADDRESS_CONTEXT_USER; - - for (guint j = 0; j < resolvers->len; j++) - { - SysprofSymbolResolver *resolver = g_ptr_array_index (resolvers, j); - GQuark tag = 0; - gchar *str; - - str = sysprof_symbol_resolver_resolve_with_context (resolver, - sample->frame.time, - sample->frame.pid, - last_context, - address, - &tag); - - if (str != NULL) - { - symbol = sysprof_callgraph_profile_intern_string_take (self, str); - if (tag != 0) - g_hash_table_insert (self->tags, (gchar *)symbol, GSIZE_TO_POINTER (tag)); - break; - } - } - } - - if (symbol != NULL) - g_array_index (resolved, SysprofAddress, len++) = POINTER_TO_U64 (symbol); - } - - if (last_context && last_context != SYSPROF_ADDRESS_CONTEXT_USER) - { - /* Kernel threads do not have a user part, so we end up here - * without ever getting a user context. If this happens, - * add the '- - kernel - - ' name, so that kernel threads - * are properly blamed on the kernel - */ - const gchar *name = sysprof_address_context_to_string (last_context); - g_array_index (resolved, SysprofAddress, len++) = POINTER_TO_U64 (name); - } - - g_array_index (resolved, guint64, len++) = POINTER_TO_U64 (cmdline); - g_array_index (resolved, guint64, len++) = POINTER_TO_U64 ("[Everything]"); - - stack_stash_add_trace (resolved_stash, (gpointer)resolved->data, len, 1); - } - - ret = TRUE; - -failure: - - if (ret == FALSE) - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "%s", - _("Sysprof was unable to generate a callgraph from the system capture.")); - else - g_task_return_pointer (task, g_steal_pointer (&resolved_stash), (GDestroyNotify)stack_stash_unref); - -cleanup: - g_clear_pointer (&resolved_stash, stack_stash_unref); - g_clear_pointer (&stash, stack_stash_unref); -} - -static void -generate_free (Generate *generate) -{ - sysprof_capture_reader_unref (generate->reader); - g_clear_object (&generate->selection); - g_slice_free (Generate, generate); -} - -static void -sysprof_callgraph_profile_generate (SysprofProfile *profile, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SysprofCallgraphProfile *self = (SysprofCallgraphProfile *)profile; - Generate *gen; - - g_autoptr(GTask) task = NULL; - - g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (self)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - gen = g_slice_new0 (Generate); - gen->reader = sysprof_capture_reader_copy (self->reader); - gen->selection = sysprof_selection_copy (self->selection); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_task_data (task, gen, (GDestroyNotify)generate_free); - g_task_run_in_thread (task, sysprof_callgraph_profile_generate_worker); -} - -static gboolean -sysprof_callgraph_profile_generate_finish (SysprofProfile *profile, - GAsyncResult *result, - GError **error) -{ - SysprofCallgraphProfile *self = (SysprofCallgraphProfile *)profile; - StackStash *stash; - - g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (self)); - g_assert (G_IS_TASK (result)); - - stash = g_task_propagate_pointer (G_TASK (result), error); - - if (stash != NULL) - { - if (stash != self->stash) - { - g_clear_pointer (&self->stash, stack_stash_unref); - self->stash = g_steal_pointer (&stash); - } - - g_clear_pointer (&stash, stack_stash_unref); - - return TRUE; - } - - return FALSE; -} - -static void -profile_iface_init (SysprofProfileInterface *iface) -{ - iface->generate = sysprof_callgraph_profile_generate; - iface->generate_finish = sysprof_callgraph_profile_generate_finish; - iface->set_reader = sysprof_callgraph_profile_set_reader; -} - -gpointer -sysprof_callgraph_profile_get_stash (SysprofCallgraphProfile *self) -{ - g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PROFILE (self), NULL); - - return self->stash; -} - -gboolean -sysprof_callgraph_profile_is_empty (SysprofCallgraphProfile *self) -{ - StackNode *root; - - g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PROFILE (self), FALSE); - - return (self->stash == NULL || - !(root = stack_stash_get_root (self->stash)) || - !root->total); -} - -GQuark -sysprof_callgraph_profile_get_tag (SysprofCallgraphProfile *self, - const gchar *symbol) -{ - g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PROFILE (self), 0); - - return GPOINTER_TO_SIZE (g_hash_table_lookup (self->tags, symbol)); -} diff --git a/src/libsysprof/sysprof-callgraph-profile.h b/src/libsysprof/sysprof-callgraph-profile.h deleted file mode 100644 index 94ed4607..00000000 --- a/src/libsysprof/sysprof-callgraph-profile.h +++ /dev/null @@ -1,51 +0,0 @@ -/* sysprof-callgraph-profile.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include "sysprof-version-macros.h" - -#include "sysprof-profile.h" -#include "sysprof-selection.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_CALLGRAPH_PROFILE (sysprof_callgraph_profile_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofCallgraphProfile, sysprof_callgraph_profile, SYSPROF, CALLGRAPH_PROFILE, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofProfile *sysprof_callgraph_profile_new (void); -SYSPROF_AVAILABLE_IN_ALL -SysprofProfile *sysprof_callgraph_profile_new_with_selection (SysprofSelection *selection); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_callgraph_profile_is_empty (SysprofCallgraphProfile *self); -SYSPROF_AVAILABLE_IN_ALL -gpointer sysprof_callgraph_profile_get_stash (SysprofCallgraphProfile *self); -SYSPROF_AVAILABLE_IN_ALL -GQuark sysprof_callgraph_profile_get_tag (SysprofCallgraphProfile *self, - const gchar *symbol); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-capture-autocleanups.h b/src/libsysprof/sysprof-capture-autocleanups.h deleted file mode 100644 index d640b7ee..00000000 --- a/src/libsysprof/sysprof-capture-autocleanups.h +++ /dev/null @@ -1,41 +0,0 @@ -/* sysprof-capture-gobject.h - * - * Copyright 2020 Endless Mobile, Inc. - * - * Author: - * - Philip Withnall - * - * 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include - -#include "sysprof-capture.h" - -G_BEGIN_DECLS - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofCaptureCondition, sysprof_capture_condition_unref) -G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofCaptureCursor, sysprof_capture_cursor_unref) -G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofCaptureReader, sysprof_capture_reader_unref) -G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofCaptureWriter, sysprof_capture_writer_unref) - -G_END_DECLS diff --git a/src/libsysprof/sysprof-capture-gobject.c b/src/libsysprof/sysprof-capture-gobject.c deleted file mode 100644 index 4e6f660d..00000000 --- a/src/libsysprof/sysprof-capture-gobject.c +++ /dev/null @@ -1,92 +0,0 @@ -/* sysprof-capture-gobject.c - * - * Copyright 2019 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 - */ - -#include "config.h" - -#include -#include - -#include "sysprof-capture-gobject.h" - -G_DEFINE_BOXED_TYPE (SysprofCaptureReader, sysprof_capture_reader, (GBoxedCopyFunc)sysprof_capture_reader_ref, (GBoxedFreeFunc)sysprof_capture_reader_unref) -G_DEFINE_BOXED_TYPE (SysprofCaptureWriter, sysprof_capture_writer, (GBoxedCopyFunc)sysprof_capture_writer_ref, (GBoxedFreeFunc)sysprof_capture_writer_unref) -G_DEFINE_BOXED_TYPE (SysprofCaptureCursor, sysprof_capture_cursor, (GBoxedCopyFunc)sysprof_capture_cursor_ref, (GBoxedFreeFunc)sysprof_capture_cursor_unref) - -SysprofCaptureReader * -sysprof_capture_reader_new_with_error (const char *filename, - GError **error) -{ - SysprofCaptureReader *ret; - - if (!(ret = sysprof_capture_reader_new (filename))) - g_set_error_literal (error, - G_FILE_ERROR, - g_file_error_from_errno (errno), - g_strerror (errno)); - - return ret; -} - -SysprofCaptureReader * -sysprof_capture_reader_new_from_fd_with_error (int fd, - GError **error) -{ - SysprofCaptureReader *ret; - - if (!(ret = sysprof_capture_reader_new_from_fd (fd))) - g_set_error_literal (error, - G_FILE_ERROR, - g_file_error_from_errno (errno), - g_strerror (errno)); - - return ret; -} - -SysprofCaptureReader * -sysprof_capture_writer_create_reader_with_error (SysprofCaptureWriter *self, - GError **error) -{ - SysprofCaptureReader *ret; - - if (!(ret = sysprof_capture_writer_create_reader (self))) - g_set_error_literal (error, - G_FILE_ERROR, - g_file_error_from_errno (errno), - g_strerror (errno)); - - return ret; -} - -bool -sysprof_capture_reader_save_as_with_error (SysprofCaptureReader *self, - const char *filename, - GError **error) -{ - if (!sysprof_capture_reader_save_as (self, filename)) - { - g_set_error_literal (error, - G_FILE_ERROR, - g_file_error_from_errno (errno), - g_strerror (errno)); - return false; - } - - return true; -} diff --git a/src/libsysprof/sysprof-capture-gobject.h b/src/libsysprof/sysprof-capture-gobject.h deleted file mode 100644 index ca6fbe23..00000000 --- a/src/libsysprof/sysprof-capture-gobject.h +++ /dev/null @@ -1,58 +0,0 @@ -/* sysprof-capture-gobject.h - * - * Copyright 2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include - -#include "sysprof-version-macros.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_CAPTURE_READER (sysprof_capture_reader_get_type()) -#define SYSPROF_TYPE_CAPTURE_WRITER (sysprof_capture_writer_get_type()) -#define SYSPROF_TYPE_CAPTURE_CURSOR (sysprof_capture_cursor_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -GType sysprof_capture_reader_get_type (void); -SYSPROF_AVAILABLE_IN_ALL -GType sysprof_capture_writer_get_type (void); -SYSPROF_AVAILABLE_IN_ALL -GType sysprof_capture_cursor_get_type (void); - -SYSPROF_AVAILABLE_IN_3_38 -SysprofCaptureReader *sysprof_capture_reader_new_with_error (const char *filename, - GError **error); -SYSPROF_AVAILABLE_IN_3_38 -SysprofCaptureReader *sysprof_capture_reader_new_from_fd_with_error (int fd, - GError **error); -SYSPROF_AVAILABLE_IN_3_38 -SysprofCaptureReader *sysprof_capture_writer_create_reader_with_error (SysprofCaptureWriter *self, - GError **error); -SYSPROF_AVAILABLE_IN_3_38 -bool sysprof_capture_reader_save_as_with_error (SysprofCaptureReader *self, - const char *filename, - GError **error); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-capture-symbol-resolver.c b/src/libsysprof/sysprof-capture-symbol-resolver.c deleted file mode 100644 index 8986c6bd..00000000 --- a/src/libsysprof/sysprof-capture-symbol-resolver.c +++ /dev/null @@ -1,137 +0,0 @@ -/* sysprof-capture-symbol-resolver.c - * - * Copyright 2019 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 G_LOG_DOMAIN "sysprof-capture-symbol-resolver" - -#include "config.h" - -#include - -#include "sysprof-capture-symbol-resolver.h" -#include "sysprof-platform.h" -#include "sysprof-private.h" -#include "sysprof-symbol-map.h" - -/** - * SECTION:sysprof-capture-symbol-resolver: - * @title: SysprofCaptureSymbolResolver - * @short_description: resolve symbols from embedded data within the capture - * - * This looks for an embedded file "__symbols__" in the capture and tries to - * decode them using the data stored within that file. - * - * This is useful when moving capture files between machines as it will allow - * the viewer machine to get access to the symbols without having to copy them - * to the local system. - * - * Since: 3.34 - */ - -struct _SysprofCaptureSymbolResolver -{ - GObject parent_instance; - SysprofSymbolMap *map; -}; - -static void symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface); - -G_DEFINE_TYPE_WITH_CODE (SysprofCaptureSymbolResolver, sysprof_capture_symbol_resolver, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SYMBOL_RESOLVER, symbol_resolver_iface_init)) - -static void -sysprof_capture_symbol_resolver_finalize (GObject *object) -{ - SysprofCaptureSymbolResolver *self = (SysprofCaptureSymbolResolver *)object; - - g_clear_pointer (&self->map, sysprof_symbol_map_free); - - G_OBJECT_CLASS (sysprof_capture_symbol_resolver_parent_class)->finalize (object); -} - -static void -sysprof_capture_symbol_resolver_class_init (SysprofCaptureSymbolResolverClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_capture_symbol_resolver_finalize; -} - -static void -sysprof_capture_symbol_resolver_init (SysprofCaptureSymbolResolver *self) -{ - self->map = sysprof_symbol_map_new (); -} - -SysprofSymbolResolver * -sysprof_capture_symbol_resolver_new (void) -{ - return g_object_new (SYSPROF_TYPE_CAPTURE_SYMBOL_RESOLVER, NULL); -} - -static void -sysprof_capture_symbol_resolver_load (SysprofSymbolResolver *resolver, - SysprofCaptureReader *reader) -{ - SysprofCaptureSymbolResolver *self = (SysprofCaptureSymbolResolver *)resolver; - gint byte_order; - gint fd; - - g_assert (SYSPROF_IS_CAPTURE_SYMBOL_RESOLVER (self)); - g_assert (reader != NULL); - - byte_order = sysprof_capture_reader_get_byte_order (reader); - - if (-1 == (fd = sysprof_memfd_create ("[symbol-decoder]"))) - return; - - if (sysprof_capture_reader_read_file_fd (reader, "__symbols__", fd)) - { - lseek (fd, 0, SEEK_SET); - sysprof_symbol_map_deserialize (self->map, byte_order, fd); - } - - close (fd); -} - -gchar * -sysprof_capture_symbol_resolver_resolve_with_context (SysprofSymbolResolver *resolver, - guint64 time, - GPid pid, - SysprofAddressContext context, - SysprofCaptureAddress address, - GQuark *tag) -{ - SysprofCaptureSymbolResolver *self = (SysprofCaptureSymbolResolver *)resolver; - const gchar *name; - - g_assert (SYSPROF_IS_CAPTURE_SYMBOL_RESOLVER (self)); - - if ((name = sysprof_symbol_map_lookup (self->map, time, pid, address, tag))) - return g_strdup (name); - - return NULL; -} - -static void -symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface) -{ - iface->load = sysprof_capture_symbol_resolver_load; - iface->resolve_with_context = sysprof_capture_symbol_resolver_resolve_with_context; -} diff --git a/src/libsysprof/sysprof-capture-symbol-resolver.h b/src/libsysprof/sysprof-capture-symbol-resolver.h deleted file mode 100644 index afb95084..00000000 --- a/src/libsysprof/sysprof-capture-symbol-resolver.h +++ /dev/null @@ -1,35 +0,0 @@ -/* sysprof-capture-symbol-resolver.h - * - * Copyright 2019 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 - */ - -#pragma once - -#include "sysprof-symbol-resolver.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_CAPTURE_SYMBOL_RESOLVER (sysprof_capture_symbol_resolver_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofCaptureSymbolResolver, sysprof_capture_symbol_resolver, SYSPROF, CAPTURE_SYMBOL_RESOLVER, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSymbolResolver *sysprof_capture_symbol_resolver_new (void); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-control-source.c b/src/libsysprof/sysprof-control-source.c deleted file mode 100644 index e39ebde8..00000000 --- a/src/libsysprof/sysprof-control-source.c +++ /dev/null @@ -1,313 +0,0 @@ -/* sysprof-control-source.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 G_LOG_DOMAIN "sysprof-control-source" - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mapped-ring-buffer.h" -#include "mapped-ring-buffer-source.h" - -#include "sysprof-control-source.h" - -#define CREATRING "CreatRing\0" -#define CREATRING_LEN 10 - -struct _SysprofControlSource -{ - GObject parent_instance; - SysprofCaptureWriter *writer; - GArray *source_ids; - -#ifdef G_OS_UNIX - GUnixConnection *conn; -#endif - - GCancellable *cancellable; - - /* Control messages are 10 bytes */ - gchar read_buf[10]; - - guint stopped : 1; - -}; - -typedef struct -{ - SysprofControlSource *self; - guint id; -} RingData; - -static void source_iface_init (SysprofSourceInterface *iface); - -G_DEFINE_TYPE_WITH_CODE (SysprofControlSource, sysprof_control_source, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init)) - -static void -ring_data_free (RingData *rd) -{ - g_clear_object (&rd->self); - g_slice_free (RingData, rd); -} - -SysprofControlSource * -sysprof_control_source_new (void) -{ - return g_object_new (SYSPROF_TYPE_CONTROL_SOURCE, NULL); -} - -static void -remove_source_id (gpointer data) -{ - guint *id = data; - g_source_remove (*id); -} - -static void -sysprof_control_source_finalize (GObject *object) -{ - SysprofControlSource *self = (SysprofControlSource *)object; - -#ifdef G_OS_UNIX - g_clear_object (&self->conn); -#endif - - if (self->source_ids->len > 0) - g_array_remove_range (self->source_ids, 0, self->source_ids->len); - - g_clear_pointer (&self->source_ids, g_array_unref); - - G_OBJECT_CLASS (sysprof_control_source_parent_class)->finalize (object); -} - -static void -sysprof_control_source_class_init (SysprofControlSourceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_control_source_finalize; -} - -static void -sysprof_control_source_init (SysprofControlSource *self) -{ - self->cancellable = g_cancellable_new (); - self->source_ids = g_array_new (FALSE, FALSE, sizeof (guint)); - g_array_set_clear_func (self->source_ids, remove_source_id); -} - -static bool -event_frame_cb (const void *data, - size_t *length, - void *user_data) -{ - const SysprofCaptureFrame *fr = data; - RingData *rd = user_data; - - g_assert (rd != NULL); - g_assert (SYSPROF_IS_CONTROL_SOURCE (rd->self)); - g_assert (rd->id > 0); - - if G_UNLIKELY (rd->self->writer == NULL || - *length < sizeof *fr || - *length < fr->len || - fr->type >= SYSPROF_CAPTURE_FRAME_LAST) - goto remove_source; - - _sysprof_capture_writer_add_raw (rd->self->writer, fr); - - *length = fr->len; - - return G_SOURCE_CONTINUE; - -remove_source: - for (guint i = 0; i < rd->self->source_ids->len; i++) - { - guint id = g_array_index (rd->self->source_ids, guint, i); - - if (id == rd->id) - { - g_array_remove_index (rd->self->source_ids, i); - break; - } - } - - return G_SOURCE_REMOVE; -} - -#ifdef G_OS_UNIX -static void -sysprof_control_source_read_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - g_autoptr(SysprofControlSource) self = user_data; - GInputStream *input_stream = (GInputStream *)object; - gssize ret; - - g_assert (SYSPROF_IS_CONTROL_SOURCE (self)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_INPUT_STREAM (input_stream)); - - ret = g_input_stream_read_finish (G_INPUT_STREAM (input_stream), result, NULL); - - if (ret == sizeof self->read_buf) - { - if (memcmp (self->read_buf, CREATRING, CREATRING_LEN) == 0) - { - g_autoptr(MappedRingBuffer) buffer = NULL; - - if ((buffer = mapped_ring_buffer_new_reader (0))) - { - int fd = mapped_ring_buffer_get_fd (buffer); - RingData *rd; - - rd = g_slice_new0 (RingData); - rd->self = g_object_ref (self); - rd->id = mapped_ring_buffer_create_source_full (buffer, - event_frame_cb, - rd, - (GDestroyNotify)ring_data_free); - - g_array_append_val (self->source_ids, rd->id); - g_unix_connection_send_fd (self->conn, fd, NULL, NULL); - } - } - - if (!g_cancellable_is_cancelled (self->cancellable)) - g_input_stream_read_async (G_INPUT_STREAM (input_stream), - self->read_buf, - sizeof self->read_buf, - G_PRIORITY_HIGH, - self->cancellable, - sysprof_control_source_read_cb, - g_object_ref (self)); - } -} -#endif - -static void -sysprof_control_source_modify_spawn (SysprofSource *source, - SysprofSpawnable *spawnable) -{ -#ifdef G_OS_UNIX - SysprofControlSource *self = (SysprofControlSource *)source; - g_autofree gchar *child_no_str = NULL; - g_autoptr(GSocketConnection) stream = NULL; - g_autoptr(GSocket) sock = NULL; - GInputStream *input_stream; - int fds[2]; - int child_no; - - g_assert (SYSPROF_IS_SOURCE (source)); - g_assert (SYSPROF_IS_SPAWNABLE (spawnable)); - - /* Create a socket pair to communicate D-Bus protocol over */ - if (socketpair (AF_LOCAL, SOCK_STREAM, 0, fds) != 0) - return; - - g_unix_set_fd_nonblocking (fds[0], TRUE, NULL); - g_unix_set_fd_nonblocking (fds[1], TRUE, NULL); - - /* @child_no is assigned the FD the child will receive. We can - * use that to set the environment vaiable of the control FD. - */ - child_no = sysprof_spawnable_take_fd (spawnable, fds[1], -1); - child_no_str = g_strdup_printf ("%d", child_no); - sysprof_spawnable_setenv (spawnable, "SYSPROF_CONTROL_FD", child_no_str); - - /* We need an IOStream for GDBusConnection to use. Since we need - * the ability to pass FDs, it must be a GUnixSocketConnection. - */ - if (!(sock = g_socket_new_from_fd (fds[0], NULL))) - { - close (fds[0]); - g_critical ("Failed to create GSocket"); - return; - } - - g_socket_set_blocking (sock, FALSE); - - stream = g_socket_connection_factory_create_connection (sock); - - g_assert (G_IS_UNIX_CONNECTION (stream)); - - self->conn = g_object_ref (G_UNIX_CONNECTION (stream)); - - input_stream = g_io_stream_get_input_stream (G_IO_STREAM (stream)); - - g_input_stream_read_async (input_stream, - self->read_buf, - sizeof self->read_buf, - G_PRIORITY_HIGH, - self->cancellable, - sysprof_control_source_read_cb, - g_object_ref (self)); -#endif -} - -static void -sysprof_control_source_stop (SysprofSource *source) -{ - SysprofControlSource *self = (SysprofControlSource *)source; - - g_assert (SYSPROF_IS_CONTROL_SOURCE (self)); - - self->stopped = TRUE; - - g_cancellable_cancel (self->cancellable); - - if (self->source_ids->len > 0) - g_array_remove_range (self->source_ids, 0, self->source_ids->len); - - sysprof_source_emit_finished (source); -} - -static void -sysprof_control_source_set_writer (SysprofSource *source, - SysprofCaptureWriter *writer) -{ - SysprofControlSource *self = (SysprofControlSource *)source; - - g_assert (SYSPROF_IS_CONTROL_SOURCE (self)); - - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); - - if (writer != NULL) - self->writer = sysprof_capture_writer_ref (writer); -} - -static void -source_iface_init (SysprofSourceInterface *iface) -{ - iface->set_writer = sysprof_control_source_set_writer; - iface->modify_spawn = sysprof_control_source_modify_spawn; - iface->stop = sysprof_control_source_stop; -} diff --git a/src/libsysprof/sysprof-control-source.h b/src/libsysprof/sysprof-control-source.h deleted file mode 100644 index b2d43467..00000000 --- a/src/libsysprof/sysprof-control-source.h +++ /dev/null @@ -1,35 +0,0 @@ -/* sysprof-control-source.h - * - * 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 - */ - -#pragma once - -#include "sysprof-source.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_CONTROL_SOURCE (sysprof_control_source_get_type()) - -SYSPROF_AVAILABLE_IN_3_38 -G_DECLARE_FINAL_TYPE (SysprofControlSource, sysprof_control_source, SYSPROF, CONTROL_SOURCE, GObject) - -SYSPROF_AVAILABLE_IN_3_38 -SysprofControlSource *sysprof_control_source_new (void); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-diskstat-source.c b/src/libsysprof/sysprof-diskstat-source.c deleted file mode 100644 index 2256a8be..00000000 --- a/src/libsysprof/sysprof-diskstat-source.c +++ /dev/null @@ -1,472 +0,0 @@ -/* sysprof-diskstat-source.c - * - * Copyright 2019 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 G_LOG_DOMAIN "sysprof-diskstat-source" - -#include "config.h" - -#include -#include -#include -#include -#include -#include - -#include "sysprof-backport-autocleanups.h" -#include "sysprof-line-reader.h" -#include "sysprof-diskstat-source.h" -#include "sysprof-helpers.h" - -#define ADD_FROM_CHAR(v, c) (((v)*10L)+((c)-'0')) - -struct _SysprofDiskstatSource -{ - GObject parent_instance; - - SysprofCaptureWriter *writer; - GArray *diskstats; - - /* FD for /proc/net/dev contents */ - gint diskstat_fd; - - /* GSource ID for polling */ - guint poll_source; - - guint ignore_next_poll : 1; -}; - -typedef struct -{ - /* Counter IDs */ - guint reads_total_id; - guint writes_total_id; - - gchar device[32]; - gint64 reads_total; - gint64 reads_merged; - gint64 reads_sectors; - gint64 reads_msec; - gint64 writes_total; - gint64 writes_merged; - gint64 writes_sectors; - gint64 writes_msec; - gint64 iops_active; - gint64 iops_msec; - gint64 iops_msec_weighted; -} Diskstat; - -enum { - /* -2 */ COLUMN_MAJOR, - /* -1 */ COLUMN_MINOR, - /* 0 */ COLUMN_NAME, - /* 1 */ COLUMN_READS_TOTAL, - /* 2 */ COLUMN_READS_MERGED, - /* 3 */ COLUMN_READS_SECTORS, - /* 4 */ COLUMN_READS_MSEC, - /* 5 */ COLUMN_WRITES_TOTAL, - /* 6 */ COLUMN_WRITES_MERGED, - /* 7 */ COLUMN_WRITES_SECTORS, - /* 8 */ COLUMN_WRITES_MSEC, - /* 9 */ COLUMN_IOPS_ACTIVE, - /* 10 */ COLUMN_IOPS_MSEC, - /* 11 */ COLUMN_IOPS_MSEC_WEIGHTED, -}; - -static void source_iface_init (SysprofSourceInterface *); - -G_DEFINE_TYPE_WITH_CODE (SysprofDiskstatSource, sysprof_diskstat_source, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init)) - -static Diskstat * -find_device_by_name (SysprofDiskstatSource *self, - const gchar *name) -{ - g_assert (SYSPROF_IS_DISKSTAT_SOURCE (self)); - g_assert (self->writer != NULL); - g_assert (name != NULL); - - for (guint i = 0; i < self->diskstats->len; i++) - { - Diskstat *diskstat = &g_array_index (self->diskstats, Diskstat, i); - - if (strcmp (name, diskstat->device) == 0) - return diskstat; - } - - return NULL; -} - -static Diskstat * -register_counters_by_name (SysprofDiskstatSource *self, - const gchar *name) -{ - SysprofCaptureCounter ctr[2] = {{{0}}}; - Diskstat ds = {0}; - - g_assert (SYSPROF_IS_DISKSTAT_SOURCE (self)); - g_assert (name != NULL); - g_assert (self->writer != NULL); - - ds.reads_total_id = sysprof_capture_writer_request_counter (self->writer, 1); - ds.writes_total_id = sysprof_capture_writer_request_counter (self->writer, 1); - - g_strlcpy (ds.device, name, sizeof ds.device); - - g_strlcpy (ctr[0].category, "Disk", sizeof ctr[0].category); - g_snprintf (ctr[0].name, sizeof ctr[0].name, "Total Reads (%s)", name); - g_strlcpy (ctr[0].description, name, sizeof ctr[0].description); - ctr[0].id = ds.reads_total_id; - ctr[0].type = SYSPROF_CAPTURE_COUNTER_INT64; - ctr[0].value.v64 = 0; - - g_strlcpy (ctr[1].category, "Disk", sizeof ctr[1].category); - g_snprintf (ctr[1].name, sizeof ctr[1].name, "Total Writes (%s)", name); - g_strlcpy (ctr[1].description, name, sizeof ctr[1].description); - ctr[1].id = ds.writes_total_id; - ctr[1].type = SYSPROF_CAPTURE_COUNTER_INT64; - ctr[1].value.v64 = 1; - - sysprof_capture_writer_define_counters (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - -1, - ctr, G_N_ELEMENTS (ctr)); - - g_array_append_val (self->diskstats, ds); - return &g_array_index (self->diskstats, Diskstat, self->diskstats->len - 1); -} - -static gboolean -sysprof_diskstat_source_get_is_ready (SysprofSource *source) -{ - return TRUE; -} - -static gboolean -sysprof_diskstat_source_poll_cb (gpointer data) -{ - g_autoptr(SysprofLineReader) reader = NULL; - SysprofDiskstatSource *self = data; - g_autoptr(GArray) counters = NULL; - g_autoptr(GArray) values = NULL; - gchar buf[4096*4]; - Diskstat *found; - gint64 combined_reads_total = 0; - gint64 combined_writes_total = 0; - gssize len; - gsize line_len; - gchar *line; - - g_assert (SYSPROF_IS_DISKSTAT_SOURCE (self)); - - if (self->diskstat_fd == -1) - { - self->poll_source = 0; - return G_SOURCE_REMOVE; - } - - /* Seek to 0 forces reload of data */ - lseek (self->diskstat_fd, 0, SEEK_SET); - - len = read (self->diskstat_fd, buf, sizeof buf - 1); - - /* Bail for now unless we read enough data */ - if (len > 0) - buf[len] = 0; - else - return G_SOURCE_CONTINUE; - - counters = g_array_new (FALSE, FALSE, sizeof (guint)); - values = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounterValue)); - reader = sysprof_line_reader_new (buf, len); - -#if 0 -Entries looks like this... ------------------------------------------------------------------------------------------------------------------------------- - 259 0 nvme0n1 455442 37 15274065 82579 714090 524038 44971827 1078532 0 244176 957543 0 0 0 0 - 259 1 nvme0n1p1 403 0 10411 102 61 39 33171 72 0 61 53 0 0 0 0 - 259 2 nvme0n1p2 138 0 9594 29 7 1 64 20 0 49 24 0 0 0 0 - 259 3 nvme0n1p3 454820 37 15249700 82432 688488 523998 44938592 1038914 0 237085 924545 0 0 0 0 - 253 0 dm-0 454789 0 15246924 107870 1238001 0 44938592 7873687 0 248354 7981557 0 0 0 0 - 253 1 dm-1 92473 0 4824170 27656 179001 0 5876760 1378161 0 45291 1405817 0 0 0 0 - 253 2 dm-2 206 0 5552 100 240 0 1920 297 0 219 397 0 0 0 0 - 253 3 dm-3 362064 0 10414850 80861 1058760 0 39245920 6496983 0 206426 6577844 0 0 0 0 ------------------------------------------------------------------------------------------------------------------------------- -#endif - - while ((line = (gchar *)sysprof_line_reader_next (reader, &line_len))) - { - Diskstat ds = {0}; - gint64 dummy = 0; - gint column = COLUMN_MAJOR; - - line[line_len] = 0; - - /* Skip past initial space */ - while (g_ascii_isspace (*line)) - line++; - - for (const gchar *ptr = line; *ptr; ptr++) - { - gchar ch; - - /* Skip past space and advance to next column */ - if (g_ascii_isspace (*ptr)) - { - while (g_ascii_isspace (*ptr)) - ptr++; - column++; - } - - ch = *ptr; - - switch (column) - { - case COLUMN_MAJOR: - case COLUMN_MINOR: - default: - dummy = ADD_FROM_CHAR (dummy, ch); - break; - - case COLUMN_NAME: - { - guint j; - - for (j = 0; j < sizeof ds.device && ds.device[j] != 0; j++) { /* Do Nothing */ } - if (j < sizeof ds.device) - ds.device[j] = ch; - ds.device[sizeof ds.device - 1] = 0; - - break; - } - - case COLUMN_READS_TOTAL: - ds.reads_total = ADD_FROM_CHAR (ds.reads_total, ch); - break; - - case COLUMN_READS_MERGED: - ds.reads_merged = ADD_FROM_CHAR (ds.reads_merged, ch); - break; - - case COLUMN_READS_SECTORS: - ds.reads_sectors = ADD_FROM_CHAR (ds.reads_sectors, ch); - break; - - case COLUMN_READS_MSEC: - ds.reads_msec = ADD_FROM_CHAR (ds.reads_msec, ch); - break; - - case COLUMN_WRITES_TOTAL: - ds.writes_total = ADD_FROM_CHAR (ds.writes_total, ch); - break; - - case COLUMN_WRITES_MERGED: - ds.writes_merged = ADD_FROM_CHAR (ds.writes_merged, ch); - break; - - case COLUMN_WRITES_SECTORS: - ds.writes_sectors = ADD_FROM_CHAR (ds.writes_sectors, ch); - break; - - case COLUMN_WRITES_MSEC: - ds.writes_msec = ADD_FROM_CHAR (ds.writes_msec, ch); - break; - - case COLUMN_IOPS_ACTIVE: - ds.iops_active = ADD_FROM_CHAR (ds.iops_active, ch); - break; - - case COLUMN_IOPS_MSEC: - ds.iops_msec = ADD_FROM_CHAR (ds.iops_msec, ch); - break; - - case COLUMN_IOPS_MSEC_WEIGHTED: - ds.iops_msec_weighted = ADD_FROM_CHAR (ds.iops_msec_weighted, ch); - break; - } - } - - g_strstrip (ds.device); - - if (ds.device[0]) - { - gint64 reads_total; - gint64 writes_total; - - if (!(found = find_device_by_name (self, ds.device))) - found = register_counters_by_name (self, ds.device); - - /* Stash new value, based on diff from previous */ - reads_total = ds.reads_total - found->reads_total; - writes_total = ds.writes_total - found->writes_total; - - g_array_append_val (counters, found->reads_total_id); - g_array_append_val (values, reads_total); - - g_array_append_val (counters, found->writes_total_id); - g_array_append_val (values, writes_total); - - /* Update combined values */ - combined_reads_total += reads_total; - combined_writes_total += writes_total; - - /* Save current value for diff on next poll */ - found->reads_total = ds.reads_total; - found->writes_total = ds.writes_total; - } - } - - if (!(found = find_device_by_name (self, "Combined"))) - found = register_counters_by_name (self, "Combined"); - - g_array_append_val (counters, found->reads_total_id); - g_array_append_val (values, combined_reads_total); - - g_array_append_val (counters, found->writes_total_id); - g_array_append_val (values, combined_writes_total); - - if (self->ignore_next_poll) - self->ignore_next_poll = FALSE; - else - sysprof_capture_writer_set_counters (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - -1, - (const guint *)(gpointer)counters->data, - (const SysprofCaptureCounterValue *)(gpointer)values->data, - counters->len); - - return G_SOURCE_CONTINUE; -} - -static void -sysprof_diskstat_source_prepare (SysprofSource *source) -{ - SysprofDiskstatSource *self = (SysprofDiskstatSource *)source; - g_autoptr(GError) error = NULL; - - g_assert (SYSPROF_IS_DISKSTAT_SOURCE (self)); - - self->diskstat_fd = g_open ("/proc/diskstats", O_RDONLY, 0); - - if (self->diskstat_fd == -1) - { - int errsv = errno; - error = g_error_new (G_FILE_ERROR, - g_file_error_from_errno (errsv), - "%s", - g_strerror (errsv)); - sysprof_source_emit_failed (source, error); - return; - } - - self->ignore_next_poll = TRUE; - sysprof_diskstat_source_poll_cb (self); - - sysprof_source_emit_ready (source); -} - -static void -sysprof_diskstat_source_start (SysprofSource *source) -{ - SysprofDiskstatSource *self = (SysprofDiskstatSource *)source; - - g_assert (SYSPROF_IS_DISKSTAT_SOURCE (self)); - - self->poll_source = g_timeout_add (200, sysprof_diskstat_source_poll_cb, self); - - /* Poll immediately */ - sysprof_diskstat_source_poll_cb (self); -} - -static void -sysprof_diskstat_source_stop (SysprofSource *source) -{ - SysprofDiskstatSource *self = (SysprofDiskstatSource *)source; - - g_assert (SYSPROF_IS_DISKSTAT_SOURCE (self)); - - /* Poll one last time */ - sysprof_diskstat_source_poll_cb (self); - - g_clear_handle_id (&self->poll_source, g_source_remove); - - sysprof_source_emit_finished (source); -} - -static void -sysprof_diskstat_source_set_writer (SysprofSource *source, - SysprofCaptureWriter *writer) -{ - SysprofDiskstatSource *self = (SysprofDiskstatSource *)source; - - g_assert (SYSPROF_IS_DISKSTAT_SOURCE (self)); - g_assert (writer != NULL); - - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); - self->writer = sysprof_capture_writer_ref (writer); -} - -static void -source_iface_init (SysprofSourceInterface *iface) -{ - iface->get_is_ready = sysprof_diskstat_source_get_is_ready; - iface->prepare = sysprof_diskstat_source_prepare; - iface->set_writer = sysprof_diskstat_source_set_writer; - iface->start = sysprof_diskstat_source_start; - iface->stop = sysprof_diskstat_source_stop; -} - -static void -sysprof_diskstat_source_finalize (GObject *object) -{ - SysprofDiskstatSource *self = (SysprofDiskstatSource *)object; - - g_clear_pointer (&self->diskstats, g_array_unref); - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); - - if (self->diskstat_fd != -1) - { - close (self->diskstat_fd); - self->diskstat_fd = -1; - } - - G_OBJECT_CLASS (sysprof_diskstat_source_parent_class)->finalize (object); -} - -static void -sysprof_diskstat_source_class_init (SysprofDiskstatSourceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_diskstat_source_finalize; -} - -static void -sysprof_diskstat_source_init (SysprofDiskstatSource *self) -{ - self->diskstats = g_array_new (FALSE, FALSE, sizeof (Diskstat)); -} - -SysprofSource * -sysprof_diskstat_source_new (void) -{ - return g_object_new (SYSPROF_TYPE_DISKSTAT_SOURCE, NULL); -} diff --git a/src/libsysprof/sysprof-diskstat-source.h b/src/libsysprof/sysprof-diskstat-source.h deleted file mode 100644 index 58386e7a..00000000 --- a/src/libsysprof/sysprof-diskstat-source.h +++ /dev/null @@ -1,35 +0,0 @@ -/* sysprof-diskstat-source.h - * - * Copyright 2019 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 - */ - -#pragma once - -#include "sysprof-source.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_DISKSTAT_SOURCE (sysprof_diskstat_source_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofDiskstatSource, sysprof_diskstat_source, SYSPROF, DISKSTAT_SOURCE, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSource *sysprof_diskstat_source_new (void); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-elf-symbol-resolver-private.h b/src/libsysprof/sysprof-elf-symbol-resolver-private.h deleted file mode 100644 index 77b65ff9..00000000 --- a/src/libsysprof/sysprof-elf-symbol-resolver-private.h +++ /dev/null @@ -1,33 +0,0 @@ -/* sysprof-elf-symbol-resolver-private.h - * - * Copyright 2021 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 - */ - -#pragma once - -#include "sysprof-elf-symbol-resolver.h" - -G_BEGIN_DECLS - -char *_sysprof_elf_symbol_resolver_resolve_path (SysprofElfSymbolResolver *self, - GPid pid, - const char *path); -const char *_sysprof_elf_symbol_resolver_get_pid_kind (SysprofElfSymbolResolver *self, - GPid pid); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-elf-symbol-resolver.c b/src/libsysprof/sysprof-elf-symbol-resolver.c deleted file mode 100644 index bd9b82c5..00000000 --- a/src/libsysprof/sysprof-elf-symbol-resolver.c +++ /dev/null @@ -1,686 +0,0 @@ -/* sysprof-elf-symbol-resolver.c - * - * Copyright 2016-2019 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 - */ - -#include "config.h" - -#include -#include - -#include "binfile.h" -#include "elfparser.h" -#include "sysprof-elf-symbol-resolver.h" -#include "sysprof-flatpak.h" -#include "sysprof-map-lookaside.h" -#include "sysprof-path-resolver.h" -#include "sysprof-podman.h" -#include "sysprof-symbol-resolver-private.h" - -typedef enum -{ - PROCESS_KIND_STANDARD, - PROCESS_KIND_FLATPAK, - PROCESS_KIND_PODMAN, -} ProcessKind; - -typedef struct -{ - char *on_host; - char *in_process; - int layer; -} ProcessOverlay; - -typedef struct -{ - SysprofMapLookaside *lookaside; - SysprofPathResolver *resolver; - GByteArray *mountinfo_data; - GArray *overlays; - char **debug_dirs; - char *info; - int pid; - guint kind : 2; -} ProcessInfo; - -struct _SysprofElfSymbolResolver -{ - GObject parent_instance; - - GHashTable *processes; - GStringChunk *chunks; - GHashTable *bin_files; - GHashTable *tag_cache; -}; - -static void symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface); - -G_DEFINE_TYPE_EXTENDED (SysprofElfSymbolResolver, - sysprof_elf_symbol_resolver, - G_TYPE_OBJECT, - 0, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SYMBOL_RESOLVER, - symbol_resolver_iface_init)) - -static void -process_info_free (gpointer data) -{ - ProcessInfo *pi = data; - - if (pi != NULL) - { - g_clear_pointer (&pi->lookaside, sysprof_map_lookaside_free); - g_clear_pointer (&pi->resolver, _sysprof_path_resolver_free); - g_clear_pointer (&pi->mountinfo_data, g_byte_array_unref); - g_clear_pointer (&pi->overlays, g_array_unref); - g_clear_pointer (&pi->debug_dirs, g_strfreev); - g_clear_pointer (&pi->info, g_free); - g_slice_free (ProcessInfo, pi); - } -} - -static const char * const * -process_info_get_debug_dirs (const ProcessInfo *pi) -{ - static const char *standard[] = { "/usr/lib/debug", NULL }; - - if (pi->debug_dirs) - return (const char * const *) pi->debug_dirs; - - return standard; -} - -static void -sysprof_elf_symbol_resolver_finalize (GObject *object) -{ - SysprofElfSymbolResolver *self = (SysprofElfSymbolResolver *)object; - - g_clear_pointer (&self->bin_files, g_hash_table_unref); - g_clear_pointer (&self->tag_cache, g_hash_table_unref); - g_clear_pointer (&self->processes, g_hash_table_unref); - g_clear_pointer (&self->chunks, g_string_chunk_free); - - G_OBJECT_CLASS (sysprof_elf_symbol_resolver_parent_class)->finalize (object); -} - -static void -sysprof_elf_symbol_resolver_class_init (SysprofElfSymbolResolverClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_elf_symbol_resolver_finalize; -} - -static void -sysprof_elf_symbol_resolver_init (SysprofElfSymbolResolver *self) -{ - self->chunks = g_string_chunk_new (4096); - self->processes = g_hash_table_new_full (NULL, NULL, NULL, process_info_free); - self->bin_files = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - (GDestroyNotify)bin_file_free); - self->tag_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); -} - -static ProcessInfo * -sysprof_elf_symbol_resolver_get_process (SysprofElfSymbolResolver *self, - int pid) -{ - ProcessInfo *pi; - - g_assert (SYSPROF_IS_ELF_SYMBOL_RESOLVER (self)); - - if (!(pi = g_hash_table_lookup (self->processes, GINT_TO_POINTER (pid)))) - { - pi = g_slice_new0 (ProcessInfo); - pi->pid = pid; - g_hash_table_insert (self->processes, GINT_TO_POINTER (pid), pi); - } - - return pi; -} - -static void -sysprof_elf_symbol_resolver_load (SysprofSymbolResolver *resolver, - SysprofCaptureReader *reader) -{ - SysprofElfSymbolResolver *self = (SysprofElfSymbolResolver *)resolver; - static const guint8 zero[1] = {0}; - SysprofCaptureFrameType type; - g_autoptr(GByteArray) mounts = NULL; - g_autofree char *mounts_data = NULL; - GHashTableIter iter; - ProcessInfo *pi; - gpointer k, v; - - g_assert (SYSPROF_IS_ELF_SYMBOL_RESOLVER (self)); - g_assert (reader != NULL); - - g_hash_table_remove_all (self->processes); - - /* First we need to load all the /proc/{pid}/mountinfo files so that - * we can discover what files within the processes filesystem namespace - * were mapped and where. We can use that information later to build - * path resolvers that let us locate the files from the host. - */ - sysprof_capture_reader_reset (reader); - while (sysprof_capture_reader_peek_type (reader, &type)) - { - if (type == SYSPROF_CAPTURE_FRAME_FILE_CHUNK) - { - const SysprofCaptureFileChunk *ev; - int out_pid; - - if (!(ev = sysprof_capture_reader_read_file (reader))) - break; - - pi = sysprof_elf_symbol_resolver_get_process (self, ev->frame.pid); - - if (strcmp (ev->path, "/.flatpak-info") == 0) - { - pi->kind = PROCESS_KIND_FLATPAK; - g_free (pi->info); - pi->info = g_strndup ((char *)ev->data, ev->len); - } - else if (strcmp (ev->path, "/run/.containerenv") == 0) - { - pi->kind = PROCESS_KIND_PODMAN; - g_free (pi->info); - pi->info = g_strndup ((char *)ev->data, ev->len); - } - else if (g_str_has_prefix (ev->path, "/proc/") && - g_str_has_suffix (ev->path, "/mountinfo") && - sscanf (ev->path, "/proc/%u/mountinfo", &out_pid) == 1) - { - if (pi->mountinfo_data == NULL) - pi->mountinfo_data = g_byte_array_new (); - if (ev->len) - g_byte_array_append (pi->mountinfo_data, ev->data, ev->len); - } - else if (g_str_equal (ev->path, "/proc/mounts")) - { - if (mounts == NULL) - mounts = g_byte_array_new (); - if (ev->len) - g_byte_array_append (mounts, ev->data, ev->len); - } - } - else if (type == SYSPROF_CAPTURE_FRAME_OVERLAY) - { - const SysprofCaptureOverlay *ev; - ProcessOverlay ov; - - if (!(ev = sysprof_capture_reader_read_overlay (reader))) - break; - - ov.on_host = g_string_chunk_insert_const (self->chunks, ev->data); - ov.in_process = g_string_chunk_insert_const (self->chunks, &ev->data[ev->src_len+1]); - ov.layer = ev->layer; - - pi = sysprof_elf_symbol_resolver_get_process (self, ev->frame.pid); - if (pi->overlays == NULL) - pi->overlays = g_array_new (FALSE, FALSE, sizeof (ProcessOverlay)); - g_array_append_val (pi->overlays, ov); - } - else - { - if (!sysprof_capture_reader_skip (reader)) - break; - } - } - - /* Now make sure we have access to /proc/mounts data. If we do not find it - * within the capture, assume we're running on the same host. - */ - if (mounts != NULL) - { - g_byte_array_append (mounts, zero, 1); - mounts_data = (char *)g_byte_array_free (g_steal_pointer (&mounts), FALSE); - } - - if (mounts_data == NULL) - g_file_get_contents ("/proc/mounts", &mounts_data, NULL, NULL); - - /* Now that we loaded all the mountinfo data, we can create path resolvers - * for each of the processes. Once we have that data we can walk the file - * again to load the map events. - */ - g_hash_table_iter_init (&iter, self->processes); - while (g_hash_table_iter_next (&iter, &k, &v)) - { - pi = v; - - if (pi->mountinfo_data == NULL) - continue; - - g_byte_array_append (pi->mountinfo_data, zero, 1); - - pi->resolver = _sysprof_path_resolver_new (mounts_data, - (const char *)pi->mountinfo_data->data); - - if (pi->overlays != NULL) - { - for (guint i = 0; i < pi->overlays->len; i++) - { - const ProcessOverlay *ov = &g_array_index (pi->overlays, ProcessOverlay, i); - _sysprof_path_resolver_add_overlay (pi->resolver, ov->in_process, ov->on_host, ov->layer); - } - } - - if (pi->kind == PROCESS_KIND_FLATPAK) - { - if (pi->info != NULL) - { - g_autoptr(GKeyFile) keyfile = g_key_file_new (); - - if (g_key_file_load_from_data (keyfile, pi->info, (gsize)-1, 0, NULL)) - { - if (g_key_file_has_group (keyfile, "Instance")) - { - g_autofree gchar *app_path = g_key_file_get_string (keyfile, "Instance", "app-path", NULL); - g_autofree gchar *runtime_path = g_key_file_get_string (keyfile, "Instance", "runtime-path", NULL); - g_autofree gchar *branch = g_key_file_get_string (keyfile, "Instance", "branch", NULL); - g_autofree gchar *arch = g_key_file_get_string (keyfile, "Instance", "arch", NULL); - g_autofree gchar *app_name = g_key_file_get_string (keyfile, "Application", "name", NULL); - g_autofree gchar *manifest_dir = g_path_get_dirname (app_path); - g_autofree gchar *manifest_path = g_build_filename (manifest_dir, "metadata", NULL); - g_autoptr(GKeyFile) manifest = g_key_file_new (); - GPtrArray *dirs = g_ptr_array_new (); - - /* TODO: extensions */ - g_ptr_array_add (dirs, g_build_filename (app_path, "lib", "debug", NULL)); - g_ptr_array_add (dirs, g_build_filename (runtime_path, "lib", "debug", NULL)); - - /* Try to figure out flatpak runtime debug symbol paths. */ - if (g_key_file_load_from_file (manifest, manifest_path, 0, NULL)) - { - /* Add the SDK debug extension. */ - g_autofree gchar *sdk = g_key_file_get_string (manifest, "Application", "sdk", NULL); - if (sdk) - { - /* Go from a string like "org.gnome.Sdk/x86_64/41" to "org.gnome.Sdk.Debug/x86_64/41". */ - g_autoptr(GString) debug = g_string_new (sdk); - g_string_replace (debug, "/", ".Debug/", 1); - - /* Construct a path like "/var/lib/flatpak/runtime/org.gnome.Sdk.Debug/x86_64/41/active/files". */ - g_ptr_array_add (dirs, g_build_filename ("/var/lib/flatpak/runtime", debug->str, "active/files", NULL)); - } - - /* Add the app's debug extension. */ - if (app_name && branch && arch) - { - /* Go from a string like "org.gnome.TextEditor" to "org.gnome.TextEditor.Debug". */ - g_autoptr(GString) debug = g_string_new (app_name); - g_string_append (debug, ".Debug"); - - /* Construct a path like "/var/lib/flatpak/runtime/org.gnome.TextEditor.Debug/x86_64/master/active/files". */ - g_ptr_array_add (dirs, g_build_filename ("/var/lib/flatpak/runtime", debug->str, arch, branch, "active/files", NULL)); - } - } - - g_ptr_array_add (dirs, 0); - pi->debug_dirs = (gchar**) g_ptr_array_free (dirs, FALSE); - } - } - } - } - else if (pi->kind == PROCESS_KIND_PODMAN) - { - pi->debug_dirs = g_new0 (gchar *, 2); - pi->debug_dirs[0] = _sysprof_path_resolver_resolve (pi->resolver, "/usr/lib/debug"); - pi->debug_dirs[1] = 0; - } - } - - /* Walk through the file again and extract maps so long as - * we have a resolver for them already. - */ - sysprof_capture_reader_reset (reader); - while (sysprof_capture_reader_peek_type (reader, &type)) - { - if (type == SYSPROF_CAPTURE_FRAME_MAP) - { - const SysprofCaptureMap *ev = sysprof_capture_reader_read_map (reader); - const char *filename = ev->filename; - g_autofree char *resolved = NULL; - SysprofMap map; - - pi = sysprof_elf_symbol_resolver_get_process (self, ev->frame.pid); - - if (pi->resolver != NULL) - { - resolved = _sysprof_path_resolver_resolve (pi->resolver, filename); - - if (resolved) - filename = resolved; - } - - map.start = ev->start; - map.end = ev->end; - map.offset = ev->offset; - map.inode = ev->inode; - map.filename = filename; - - if (pi->lookaside == NULL) - pi->lookaside = sysprof_map_lookaside_new (); - - sysprof_map_lookaside_insert (pi->lookaside, &map); - } - else - { - if (!sysprof_capture_reader_skip (reader)) - return; - } - } -} - -static bin_file_t * -sysprof_elf_symbol_resolver_get_bin_file (SysprofElfSymbolResolver *self, - const ProcessInfo *pi, - const gchar *filename) -{ - g_autofree char *alternate = NULL; - const char * const *debug_dirs; - g_autofree char *on_host = NULL; - bin_file_t *bin_file; - - g_assert (SYSPROF_IS_ELF_SYMBOL_RESOLVER (self)); - - if ((bin_file = g_hash_table_lookup (self->bin_files, filename))) - return bin_file; - - /* Debug dirs are going to be dependent on the process as different - * containers may affect where the debug symbols are installed. - */ - debug_dirs = process_info_get_debug_dirs (pi); - bin_file = bin_file_new (filename, (const char * const *)debug_dirs); - g_hash_table_insert (self->bin_files, g_strdup (filename), bin_file); - - return bin_file; -} - -static GQuark -guess_tag (SysprofElfSymbolResolver *self, - const SysprofMap *map) -{ - g_assert (map != NULL); - g_assert (map->filename != NULL); - - if (!g_hash_table_contains (self->tag_cache, map->filename)) - { - GQuark tag = 0; - - if (strstr (map->filename, "/libgobject-2.0.")) - tag = g_quark_from_static_string ("GObject"); - - else if (strstr (map->filename, "/libc.so.6")) - tag = g_quark_from_static_string ("libc"); - - else if (strstr (map->filename, "/libstdc++.so.6")) - tag = g_quark_from_static_string ("stdc++"); - - else if (strstr (map->filename, "/libglib-2.0.")) - tag = g_quark_from_static_string ("GLib"); - - else if (strstr (map->filename, "/libgio-2.0.")) - tag = g_quark_from_static_string ("Gio"); - - else if (strstr (map->filename, "/libgirepository-1.0.")) - tag = g_quark_from_static_string ("Introspection"); - - else if (strstr (map->filename, "/libgtk-4.")) - tag = g_quark_from_static_string ("Gtk 4"); - - else if (strstr (map->filename, "/libgtk-3.")) - tag = g_quark_from_static_string ("Gtk 3"); - - else if (strstr (map->filename, "/libgdk-3.")) - tag = g_quark_from_static_string ("Gdk 3"); - - else if (strstr (map->filename, "/libgtksourceview-3.0")) - tag = g_quark_from_static_string ("GtkSourceView-3"); - - else if (strstr (map->filename, "/libgtksourceview-4")) - tag = g_quark_from_static_string ("GtkSourceView-4"); - - else if (strstr (map->filename, "/libpixman-1")) - tag = g_quark_from_static_string ("Pixman"); - - else if (strstr (map->filename, "/libcairo.")) - tag = g_quark_from_static_string ("cairo"); - - else if (strstr (map->filename, "/libgstreamer-1.")) - tag = g_quark_from_static_string ("GStreamer"); - - else if (strstr (map->filename, "/libX11.")) - tag = g_quark_from_static_string ("X11"); - - else if (strstr (map->filename, "/libpango-1.0.")) - tag = g_quark_from_static_string ("Pango"); - - else if (strstr (map->filename, "/libpangocairo-1.0.")) - tag = g_quark_from_static_string ("Pango"); - - else if (strstr (map->filename, "/libpangomm-1.4.")) - tag = g_quark_from_static_string ("Pango"); - - else if (strstr (map->filename, "/libpangoft2-1.0")) - tag = g_quark_from_static_string ("Pango"); - - else if (strstr (map->filename, "/libpangoxft-1.0.")) - tag = g_quark_from_static_string ("Pango"); - - else if (strstr (map->filename, "/libclutter-")) - tag = g_quark_from_static_string ("Clutter"); - - else if (strstr (map->filename, "/libcogl.") || - strstr (map->filename, "/libcogl-")) - tag = g_quark_from_static_string ("Cogl"); - - else if (strstr (map->filename, "/libffi.")) - tag = g_quark_from_static_string ("libffi"); - - else if (strstr (map->filename, "/libwayland-")) - tag = g_quark_from_static_string ("Wayland"); - - else if (strstr (map->filename, "/libinput.")) - tag = g_quark_from_static_string ("libinput"); - - else if (strstr (map->filename, "/libgjs.")) - tag = g_quark_from_static_string ("Gjs"); - - else if (strstr (map->filename, "/libmozjs-")) - tag = g_quark_from_static_string ("MozJS"); - - else if (strstr (map->filename, "/libGL.")) - tag = g_quark_from_static_string ("GL"); - - else if (strstr (map->filename, "/libEGL.")) - tag = g_quark_from_static_string ("EGL"); - - g_hash_table_insert (self->tag_cache, - g_strdup (map->filename), - GSIZE_TO_POINTER (tag)); - } - - return GPOINTER_TO_SIZE (g_hash_table_lookup (self->tag_cache, map->filename)); -} - -gboolean -sysprof_elf_symbol_resolver_resolve_full (SysprofElfSymbolResolver *self, - guint64 time, - GPid pid, - SysprofAddressContext context, - SysprofCaptureAddress address, - SysprofCaptureAddress *begin, - SysprofCaptureAddress *end, - gchar **name, - GQuark *tag) -{ - const bin_symbol_t *bin_sym; - const gchar *bin_sym_name; - const SysprofMap *map; - ProcessInfo *pi; - bin_file_t *bin_file; - gulong ubegin; - gulong uend; - - g_assert (SYSPROF_IS_ELF_SYMBOL_RESOLVER (self)); - g_assert (name != NULL); - g_assert (begin != NULL); - g_assert (end != NULL); - - *name = NULL; - - if (context != SYSPROF_ADDRESS_CONTEXT_USER) - return FALSE; - - if (!(pi = g_hash_table_lookup (self->processes, GINT_TO_POINTER (pid)))) - return FALSE; - - if (pi->lookaside == NULL) - return FALSE; - - if (!(map = sysprof_map_lookaside_lookup (pi->lookaside, address))) - return FALSE; - - address -= map->start; - address += map->offset; - - bin_file = sysprof_elf_symbol_resolver_get_bin_file (self, pi, map->filename); - - g_assert (bin_file != NULL); - - /* PERF_RECORD_MMAP doesn't provide an inode, so we can't rely on that - * until we can get PERF_RECORD_MMAP2. - */ - if G_UNLIKELY (map->inode && !bin_file_check_inode (bin_file, map->inode)) - { - *name = g_strdup_printf ("%s: inode mismatch", map->filename); - return TRUE; - } - - bin_sym = bin_file_lookup_symbol (bin_file, address); - bin_sym_name = bin_symbol_get_name (bin_file, bin_sym); - - if G_LIKELY (map->filename) - *tag = guess_tag (self, map); - - *name = elf_demangle (bin_sym_name); - bin_symbol_get_address_range (bin_file, bin_sym, &ubegin, &uend); - - *begin = ubegin; - *end = uend; - - return TRUE; -} - -static gchar * -sysprof_elf_symbol_resolver_resolve_with_context (SysprofSymbolResolver *resolver, - guint64 time, - GPid pid, - SysprofAddressContext context, - SysprofCaptureAddress address, - GQuark *tag) -{ - gchar *name = NULL; - SysprofCaptureAddress begin, end; - - /* If not user context, nothing we can do here */ - if (context != SYSPROF_ADDRESS_CONTEXT_USER) - return NULL; - - /* If this is a jitmap entry, bail early to save some cycles */ - if ((address & SYSPROF_CAPTURE_JITMAP_MARK) == SYSPROF_CAPTURE_JITMAP_MARK) - return NULL; - - sysprof_elf_symbol_resolver_resolve_full (SYSPROF_ELF_SYMBOL_RESOLVER (resolver), - time, - pid, - context, - address, - &begin, - &end, - &name, - tag); - - return g_steal_pointer (&name); -} - -static void -symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface) -{ - iface->load = sysprof_elf_symbol_resolver_load; - iface->resolve_with_context = sysprof_elf_symbol_resolver_resolve_with_context; -} - -SysprofSymbolResolver * -sysprof_elf_symbol_resolver_new (void) -{ - return g_object_new (SYSPROF_TYPE_ELF_SYMBOL_RESOLVER, NULL); -} - -void -sysprof_elf_symbol_resolver_add_debug_dir (SysprofElfSymbolResolver *self, - const gchar *debug_dir) -{ - /* Do Nothing */ - /* XXX: Mark as deprecated post 41 or remove with Gtk4 port */ -} - -char * -_sysprof_elf_symbol_resolver_resolve_path (SysprofElfSymbolResolver *self, - GPid pid, - const char *path) -{ - ProcessInfo *pi; - - g_return_val_if_fail (SYSPROF_IS_ELF_SYMBOL_RESOLVER (self), NULL); - - if (!(pi = g_hash_table_lookup (self->processes, GINT_TO_POINTER (pid)))) - return NULL; - - if (pi->resolver == NULL) - return NULL; - - return _sysprof_path_resolver_resolve (pi->resolver, path); -} - -const char * -_sysprof_elf_symbol_resolver_get_pid_kind (SysprofElfSymbolResolver *self, - GPid pid) -{ - ProcessInfo *pi; - - g_return_val_if_fail (SYSPROF_IS_ELF_SYMBOL_RESOLVER (self), NULL); - - if (!(pi = g_hash_table_lookup (self->processes, GINT_TO_POINTER (pid)))) - return "unknown"; - - if (pi->kind == PROCESS_KIND_FLATPAK) - return "Flatpak"; - - if (pi->kind == PROCESS_KIND_PODMAN) - return "Podman"; - - if (pi->kind == PROCESS_KIND_STANDARD) - return "Standard"; - - return "unknown"; -} diff --git a/src/libsysprof/sysprof-elf-symbol-resolver.h b/src/libsysprof/sysprof-elf-symbol-resolver.h deleted file mode 100644 index 6b8e5fdc..00000000 --- a/src/libsysprof/sysprof-elf-symbol-resolver.h +++ /dev/null @@ -1,53 +0,0 @@ -/* sysprof-elf-symbol-resolver.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include "sysprof-symbol-resolver.h" -#include "sysprof-version-macros.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_ELF_SYMBOL_RESOLVER (sysprof_elf_symbol_resolver_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofElfSymbolResolver, sysprof_elf_symbol_resolver, SYSPROF, ELF_SYMBOL_RESOLVER, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSymbolResolver *sysprof_elf_symbol_resolver_new (void); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_elf_symbol_resolver_add_debug_dir (SysprofElfSymbolResolver *self, - const gchar *debug_dir); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_elf_symbol_resolver_resolve_full (SysprofElfSymbolResolver *self, - guint64 time, - GPid pid, - SysprofAddressContext context, - SysprofCaptureAddress address, - SysprofCaptureAddress *begin, - SysprofCaptureAddress *end, - gchar **name, - GQuark *tag); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-flatpak.c b/src/libsysprof/sysprof-flatpak.c deleted file mode 100644 index 378127c5..00000000 --- a/src/libsysprof/sysprof-flatpak.c +++ /dev/null @@ -1,158 +0,0 @@ -/* sysprof-flatpak.c - * - * Copyright 2019 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 - */ - -#include "config.h" - -#include - -#include "sysprof-flatpak.h" - -#define ETC_INSTALLATIONS_D "/etc/flatpak/installations.d" - -static void -add_from_installations_d (GPtrArray *ret, - const gchar *path, - const gchar *prefix) -{ - g_autoptr(GDir) dir = NULL; - - g_assert (ret != NULL); - g_assert (path != NULL); - - /* Now look at /etc/flatpak/installations.d for keyfiles with Path= */ - if ((dir = g_dir_open (path, 0, NULL))) - { - const gchar *name; - - while ((name = g_dir_read_name (dir))) - { - g_autofree gchar *key_path = g_build_filename (path, name, NULL); - g_autoptr(GKeyFile) kf = g_key_file_new (); - - if (g_key_file_load_from_file (kf, key_path, G_KEY_FILE_NONE, NULL)) - { - g_auto(GStrv) groups = g_key_file_get_groups (kf, NULL); - - for (guint i = 0; groups[i]; i++) - { - if (g_key_file_has_key (kf, groups[i], "Path", NULL)) - { - gchar *val = g_key_file_get_string (kf, groups[i], "Path", NULL); - - if (val) - { - if (prefix) - g_ptr_array_add (ret, g_build_filename (prefix, val, NULL)); - else - g_ptr_array_add (ret, g_steal_pointer (&val)); - } - } - } - } - } - } -} - -gchar ** -get_installations (void) -{ - GPtrArray *ret = g_ptr_array_new (); - - /* We might be running from a container, so ignore XDG_DATA_HOME as - * that will likely be different that what we care about the host. - * TODO: Can we find a way to support non-standard XDG_DATA_HOME? - */ - g_ptr_array_add (ret, g_build_filename (g_get_home_dir (), ".local", "share", "flatpak", NULL)); - g_ptr_array_add (ret, g_strdup ("/var/lib/flatpak")); - - add_from_installations_d (ret, ETC_INSTALLATIONS_D, NULL); - add_from_installations_d (ret, "/var/run/host" ETC_INSTALLATIONS_D, "/var/run/host"); - - g_ptr_array_add (ret, NULL); - return (gchar **)g_ptr_array_free (ret, FALSE); -} - -static void -get_arch (gchar *out, - gsize len) -{ - struct utsname u; - uname (&u); - g_strlcpy (out, u.machine, len); -} - -void -_sysprof_flatpak_debug_dirs (GPtrArray *dirs) -{ - g_auto(GStrv) installs = get_installations (); - gchar arch[32]; - - g_assert (dirs != NULL); - - get_arch (arch, sizeof arch); - - g_ptr_array_add (dirs, g_strdup ("/var/run/host/usr/lib/debug")); - g_ptr_array_add (dirs, g_strdup ("/var/run/host/usr/lib32/debug")); - g_ptr_array_add (dirs, g_strdup ("/var/run/host/usr/lib64/debug")); - - /* For each of the installations, we want to look at all of the runtimes that - * exist within it. Of those runtimes, we want to limit ourselves to the active - * version of each runtime, and see if we have a deployment for the current - * system arch that contains a "lib/debug" directory. We could add more, but - * it's just too many directories. - */ - - for (guint i = 0; installs[i]; i++) - { - g_autofree gchar *repo_dir = g_build_filename (installs[i], "runtime", NULL); - g_autoptr(GDir) dir = g_dir_open (repo_dir, 0, NULL); - const gchar *name; - - if (dir == NULL) - continue; - - while ((name = g_dir_read_name (dir))) - { - g_autofree gchar *version_dir = g_build_filename (installs[i], "runtime", name, arch, NULL); - g_autoptr(GDir) vdir = g_dir_open (version_dir, 0, NULL); - const gchar *version; - - if (vdir == NULL) - continue; - - while ((version = g_dir_read_name (vdir))) - { - g_autofree gchar *lib_debug = g_build_filename (version_dir, version, "active", "files", "lib", "debug", NULL); - - if (g_file_test (lib_debug, G_FILE_TEST_EXISTS)) - g_ptr_array_add (dirs, g_steal_pointer (&lib_debug)); - } - } - } -} - -gchar ** -sysprof_flatpak_debug_dirs (void) -{ - GPtrArray *dirs = g_ptr_array_new (); - _sysprof_flatpak_debug_dirs (dirs); - g_ptr_array_add (dirs, NULL); - return (gchar **)g_ptr_array_free (dirs, FALSE); -} diff --git a/src/libsysprof/sysprof-flatpak.h b/src/libsysprof/sysprof-flatpak.h deleted file mode 100644 index 04cc0043..00000000 --- a/src/libsysprof/sysprof-flatpak.h +++ /dev/null @@ -1,29 +0,0 @@ -/* sysprof-flatpak.h - * - * Copyright 2019 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 - */ - -#pragma once - -#include - -G_BEGIN_DECLS - -gchar **sysprof_flatpak_debug_dirs (void); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-gjs-source.c b/src/libsysprof/sysprof-gjs-source.c deleted file mode 100644 index 168598dc..00000000 --- a/src/libsysprof/sysprof-gjs-source.c +++ /dev/null @@ -1,68 +0,0 @@ -/* sysprof-gjs-source.c - * - * Copyright 2019 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 G_LOG_DOMAIN "sysprof-gjs-source" - -#include "config.h" - -#include "sysprof-gjs-source.h" - -struct _SysprofGjsSource -{ - SysprofTracefdSource parent_instance; -}; - -static SysprofSourceInterface *parent_iface; - -static void -sysprof_gjs_source_modify_spawn (SysprofSource *source, - SysprofSpawnable *spawnable) -{ - sysprof_spawnable_setenv (spawnable, "GJS_ENABLE_PROFILER", "1"); - parent_iface->modify_spawn (source, spawnable); -} - -static void -source_iface_init (SysprofSourceInterface *iface) -{ - parent_iface = g_type_interface_peek_parent (iface); - - iface->modify_spawn = sysprof_gjs_source_modify_spawn; -} - -G_DEFINE_TYPE_WITH_CODE (SysprofGjsSource, sysprof_gjs_source, SYSPROF_TYPE_TRACEFD_SOURCE, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init)) - -static void -sysprof_gjs_source_class_init (SysprofGjsSourceClass *klass) -{ -} - -static void -sysprof_gjs_source_init (SysprofGjsSource *self) -{ - sysprof_tracefd_source_set_envvar (SYSPROF_TRACEFD_SOURCE (self), "GJS_TRACE_FD"); -} - -SysprofSource * -sysprof_gjs_source_new (void) -{ - return g_object_new (SYSPROF_TYPE_GJS_SOURCE, NULL); -} diff --git a/src/libsysprof/sysprof-gjs-source.h b/src/libsysprof/sysprof-gjs-source.h deleted file mode 100644 index 02316d80..00000000 --- a/src/libsysprof/sysprof-gjs-source.h +++ /dev/null @@ -1,35 +0,0 @@ -/* sysprof-gjs-source.h - * - * Copyright 2019 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 - */ - -#pragma once - -#include "sysprof-tracefd-source.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_GJS_SOURCE (sysprof_gjs_source_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofGjsSource, sysprof_gjs_source, SYSPROF, GJS_SOURCE, SysprofTracefdSource) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSource *sysprof_gjs_source_new (void); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-governor-source.c b/src/libsysprof/sysprof-governor-source.c deleted file mode 100644 index e22953f2..00000000 --- a/src/libsysprof/sysprof-governor-source.c +++ /dev/null @@ -1,318 +0,0 @@ -/* sysprof-governor-source.c - * - * Copyright 2019 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 G_LOG_DOMAIN "sysprof-governor-source" - -#include "config.h" - -#include "sysprof-governor-source.h" -#include "sysprof-helpers.h" - -struct _SysprofGovernorSource -{ - GObject parent_instance; - gchar *old_governor; - int old_paranoid; - guint disable_governor : 1; -}; - -enum { - PROP_0, - PROP_DISABLE_GOVERNOR, - N_PROPS -}; - -static void source_iface_init (SysprofSourceInterface *iface); - -G_DEFINE_TYPE_WITH_CODE (SysprofGovernorSource, sysprof_governor_source, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init)) - -static GParamSpec *properties [N_PROPS]; - -static void -sysprof_governor_source_finalize (GObject *object) -{ - SysprofGovernorSource *self = (SysprofGovernorSource *)object; - - g_clear_pointer (&self->old_governor, g_free); - - G_OBJECT_CLASS (sysprof_governor_source_parent_class)->finalize (object); -} - -static void -sysprof_governor_source_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofGovernorSource *self = SYSPROF_GOVERNOR_SOURCE (object); - - switch (prop_id) - { - case PROP_DISABLE_GOVERNOR: - g_value_set_boolean (value, sysprof_governor_source_get_disable_governor (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_governor_source_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofGovernorSource *self = SYSPROF_GOVERNOR_SOURCE (object); - - switch (prop_id) - { - case PROP_DISABLE_GOVERNOR: - sysprof_governor_source_set_disable_governor (self, g_value_get_boolean (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_governor_source_class_init (SysprofGovernorSourceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_governor_source_finalize; - object_class->get_property = sysprof_governor_source_get_property; - object_class->set_property = sysprof_governor_source_set_property; - - properties [PROP_DISABLE_GOVERNOR] = - g_param_spec_boolean ("disable-governor", - "Disable Governor", - "Disable Governor", - TRUE, - (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_governor_source_init (SysprofGovernorSource *self) -{ - self->disable_governor = FALSE; - self->old_paranoid = 2; -} - -SysprofSource * -sysprof_governor_source_new (void) -{ - return g_object_new (SYSPROF_TYPE_GOVERNOR_SOURCE, NULL); -} - -gboolean -sysprof_governor_source_get_disable_governor (SysprofGovernorSource *self) -{ - g_return_val_if_fail (SYSPROF_IS_GOVERNOR_SOURCE (self), FALSE); - - return self->disable_governor; -} - -void -sysprof_governor_source_set_disable_governor (SysprofGovernorSource *self, - gboolean disable_governor) -{ - g_return_if_fail (SYSPROF_IS_GOVERNOR_SOURCE (self)); - - disable_governor = !!disable_governor; - - if (disable_governor != self->disable_governor) - { - self->disable_governor = disable_governor; - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DISABLE_GOVERNOR]); - } -} - -static void -sysprof_governor_source_serialize (SysprofSource *source, - GKeyFile *keyfile, - const gchar *group) -{ - SysprofGovernorSource *self = (SysprofGovernorSource *)source; - - g_assert (SYSPROF_IS_GOVERNOR_SOURCE (self)); - g_assert (keyfile != NULL); - g_assert (group != NULL); - - g_key_file_set_boolean (keyfile, group, "disable-governor", self->disable_governor); -} - -static void -sysprof_governor_source_deserialize (SysprofSource *source, - GKeyFile *keyfile, - const gchar *group) -{ - SysprofGovernorSource *self = (SysprofGovernorSource *)source; - - g_assert (SYSPROF_IS_GOVERNOR_SOURCE (self)); - g_assert (keyfile != NULL); - g_assert (group != NULL); - - sysprof_governor_source_set_disable_governor (self, - g_key_file_get_boolean (keyfile, group, "disable-governor", NULL)); -} - -static void -disable_governor_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofHelpers *helpers = (SysprofHelpers *)object; - g_autoptr(SysprofGovernorSource) self = user_data; - g_autoptr(GError) error = NULL; - g_autofree gchar *old_governor = NULL; - - g_assert (SYSPROF_IS_HELPERS (helpers)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (SYSPROF_IS_GOVERNOR_SOURCE (self)); - - if (!sysprof_helpers_set_governor_finish (helpers, result, &old_governor, &error)) - g_warning ("Failed to change governor: %s", error->message); - else - self->old_governor = g_steal_pointer (&old_governor); - - sysprof_source_emit_ready (SYSPROF_SOURCE (self)); -} - -static void -disable_paranoid_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofHelpers *helpers = (SysprofHelpers *)object; - g_autoptr(SysprofGovernorSource) self = user_data; - g_autoptr(GError) error = NULL; - int old_paranoid; - - g_assert (SYSPROF_IS_HELPERS (helpers)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (SYSPROF_IS_GOVERNOR_SOURCE (self)); - - if (!sysprof_helpers_set_paranoid_finish (helpers, result, &old_paranoid, &error)) - g_debug ("Failed to change perf_event_paranoid: %s", error->message); - else - self->old_paranoid = old_paranoid; - - if (!self->disable_governor) - sysprof_source_emit_ready (SYSPROF_SOURCE (self)); - else - sysprof_helpers_set_governor_async (helpers, - "performance", - NULL, - disable_governor_cb, - g_steal_pointer (&self)); -} - -static void -sysprof_governor_source_prepare (SysprofSource *source) -{ - SysprofGovernorSource *self = (SysprofGovernorSource *)source; - SysprofHelpers *helpers = sysprof_helpers_get_default (); - - g_assert (SYSPROF_IS_GOVERNOR_SOURCE (self)); - - sysprof_helpers_set_paranoid_async (helpers, - -1, - NULL, - disable_paranoid_cb, - g_object_ref (self)); -} - -static void -enable_governor_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofHelpers *helpers = (SysprofHelpers *)object; - g_autoptr(SysprofGovernorSource) self = user_data; - g_autoptr(GError) error = NULL; - g_autofree gchar *old_governor = NULL; - - g_assert (SYSPROF_IS_HELPERS (helpers)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (SYSPROF_IS_GOVERNOR_SOURCE (self)); - - if (!sysprof_helpers_set_governor_finish (helpers, result, &old_governor, &error)) - g_warning ("Failed to change governor: %s", error->message); - - g_clear_pointer (&self->old_governor, g_free); - - sysprof_source_emit_finished (SYSPROF_SOURCE (self)); -} - -static void -enable_paranoid_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofHelpers *helpers = (SysprofHelpers *)object; - g_autoptr(SysprofGovernorSource) self = user_data; - g_autoptr(GError) error = NULL; - int old_governor; - - g_assert (SYSPROF_IS_HELPERS (helpers)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (SYSPROF_IS_GOVERNOR_SOURCE (self)); - - if (!sysprof_helpers_set_paranoid_finish (helpers, result, &old_governor, &error)) - g_debug ("Failed to change event_perf_paranoid: %s", error->message); - - if (!self->disable_governor || self->old_governor == NULL) - sysprof_source_emit_finished (SYSPROF_SOURCE (self)); - else - sysprof_helpers_set_governor_async (helpers, - self->old_governor, - NULL, - enable_governor_cb, - g_object_ref (self)); -} - -static void -sysprof_governor_source_stop (SysprofSource *source) -{ - SysprofGovernorSource *self = (SysprofGovernorSource *)source; - SysprofHelpers *helpers = sysprof_helpers_get_default (); - - g_assert (SYSPROF_IS_GOVERNOR_SOURCE (self)); - - sysprof_helpers_set_paranoid_async (helpers, - self->old_paranoid, - NULL, - enable_paranoid_cb, - g_object_ref (self)); -} - -static void -source_iface_init (SysprofSourceInterface *iface) -{ - iface->deserialize = sysprof_governor_source_deserialize; - iface->prepare = sysprof_governor_source_prepare; - iface->serialize = sysprof_governor_source_serialize; - iface->stop = sysprof_governor_source_stop; -} diff --git a/src/libsysprof/sysprof-governor-source.h b/src/libsysprof/sysprof-governor-source.h deleted file mode 100644 index 77968883..00000000 --- a/src/libsysprof/sysprof-governor-source.h +++ /dev/null @@ -1,40 +0,0 @@ -/* sysprof-governor-source.h - * - * Copyright 2019 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 - */ - -#pragma once - -#include "sysprof-source.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_GOVERNOR_SOURCE (sysprof_governor_source_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofGovernorSource, sysprof_governor_source, SYSPROF, GOVERNOR_SOURCE, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSource *sysprof_governor_source_new (void); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_governor_source_get_disable_governor (SysprofGovernorSource *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_governor_source_set_disable_governor (SysprofGovernorSource *self, - gboolean disable_governor); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-helpers.c b/src/libsysprof/sysprof-helpers.c deleted file mode 100644 index 08810858..00000000 --- a/src/libsysprof/sysprof-helpers.c +++ /dev/null @@ -1,835 +0,0 @@ -/* sysprof-helpers.c - * - * Copyright 2019 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 G_LOG_DOMAIN "sysprof-helpers" - -#include "config.h" - -#include - -#include "sysprof-helpers.h" -#include "sysprof-polkit-private.h" - -#include "helpers.h" -#include "ipc-service.h" - -struct _SysprofHelpers -{ - GObject parent_instance; - IpcService *proxy; - GQueue auth_tasks; - guint did_auth : 1; -}; - -G_DEFINE_TYPE (SysprofHelpers, sysprof_helpers, G_TYPE_OBJECT) - -static void -sysprof_helpers_finalize (GObject *object) -{ - SysprofHelpers *self = (SysprofHelpers *)object; - - g_clear_object (&self->proxy); - - G_OBJECT_CLASS (sysprof_helpers_parent_class)->finalize (object); -} - -static void -sysprof_helpers_class_init (SysprofHelpersClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_helpers_finalize; -} - -static void -sysprof_helpers_init (SysprofHelpers *self) -{ - g_autoptr(GDBusConnection) bus = NULL; - - if ((bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL))) - self->proxy = ipc_service_proxy_new_sync (bus, - G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION, - "org.gnome.Sysprof3", - "/org/gnome/Sysprof3", - NULL, NULL); -} - -SysprofHelpers * -sysprof_helpers_get_default (void) -{ - static SysprofHelpers *instance; - - if (g_once_init_enter (&instance)) - { - SysprofHelpers *self = g_object_new (SYSPROF_TYPE_HELPERS, NULL); - g_object_add_weak_pointer (G_OBJECT (self), (gpointer *)&instance); - g_once_init_leave (&instance, self); - } - - return instance; -} - -static gboolean -fail_if_no_proxy (SysprofHelpers *self, - GTask *task) -{ - g_assert (SYSPROF_IS_HELPERS (self)); - g_assert (G_IS_TASK (task)); - - if (self->proxy == NULL) - { - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_NOT_CONNECTED, - "No D-Bus proxy to communicate with daemon"); - return TRUE; - } - - return FALSE; -} - -static void -sysprof_helpers_list_processes_local_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - g_autoptr(GTask) task = user_data; - g_autoptr(GError) error = NULL; - g_autofree gint32 *processes = NULL; - gsize n_processes = 0; - - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - if (helpers_list_processes_finish (result, &processes, &n_processes, &error)) - { - g_autoptr(GVariant) ret = NULL; - - ret = g_variant_new_fixed_array (G_VARIANT_TYPE_INT32, - processes, - n_processes, - sizeof (gint32)); - g_task_return_pointer (task, - g_variant_take_ref (g_steal_pointer (&ret)), - (GDestroyNotify) g_variant_unref); - - return; - } - - g_task_return_error (task, g_steal_pointer (&error)); -} - -static void -sysprof_helpers_list_processes_cb (IpcService *service, - GAsyncResult *result, - gpointer user_data) -{ - g_autoptr(GTask) task = user_data; - g_autoptr(GVariant) processes = NULL; - g_autoptr(GError) error = NULL; - - g_assert (IPC_IS_SERVICE (service)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - if (!ipc_service_call_list_processes_finish (service, &processes, result, &error)) - helpers_list_processes_async (g_task_get_cancellable (task), - sysprof_helpers_list_processes_local_cb, - g_object_ref (task)); - else - g_task_return_pointer (task, g_steal_pointer (&processes), (GDestroyNotify) g_variant_unref); -} - -gboolean -sysprof_helpers_list_processes (SysprofHelpers *self, - GCancellable *cancellable, - gint32 **processes, - gsize *n_processes, - GError **error) -{ - g_autoptr(GVariant) fixed_ar = NULL; - - g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE); - g_return_val_if_fail (processes != NULL, FALSE); - g_return_val_if_fail (n_processes != NULL, FALSE); - - if (helpers_can_see_pids ()) - { - /* No need to query remote if we can see pids in this namespace */ - if (helpers_list_processes (processes, n_processes)) - return TRUE; - } - - if (self->proxy && ipc_service_call_list_processes_sync (self->proxy, &fixed_ar, cancellable, NULL)) - { - const gint32 *data; - gsize len; - - data = g_variant_get_fixed_array (fixed_ar, &len, sizeof (gint32)); - *processes = g_memdup2 (data, len * sizeof (gint32)); - *n_processes = len; - - return TRUE; - } - - helpers_list_processes (processes, n_processes); - - return TRUE; -} - -void -sysprof_helpers_list_processes_async (SysprofHelpers *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GTask) task = NULL; - - g_return_if_fail (SYSPROF_IS_HELPERS (self)); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_helpers_list_processes_async); - - if (self->proxy != NULL) - ipc_service_call_list_processes (self->proxy, - cancellable, - (GAsyncReadyCallback) sysprof_helpers_list_processes_cb, - g_steal_pointer (&task)); - else - helpers_list_processes_async (cancellable, - sysprof_helpers_list_processes_local_cb, - g_steal_pointer (&task)); -} - -gboolean -sysprof_helpers_list_processes_finish (SysprofHelpers *self, - GAsyncResult *result, - gint32 **processes, - gsize *n_processes, - GError **error) -{ - g_autoptr(GVariant) ret = NULL; - - g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE); - g_return_val_if_fail (G_IS_TASK (result), FALSE); - - if ((ret = g_task_propagate_pointer (G_TASK (result), error))) - { - const gint32 *p; - gsize n; - - p = g_variant_get_fixed_array (ret, &n, sizeof (gint32)); - - if (processes != NULL) - *processes = g_memdup2 (p, n * sizeof (gint32)); - - if (n_processes != NULL) - *n_processes = n; - - return TRUE; - } - - return FALSE; -} - -gboolean -sysprof_helpers_get_proc_fd (SysprofHelpers *self, - const gchar *path, - GCancellable *cancellable, - gint *out_fd, - GError **error) -{ - g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE); - g_return_val_if_fail (path != NULL, FALSE); - g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (out_fd != NULL, FALSE); - - *out_fd = -1; - - if (self->proxy != NULL) - { - g_autoptr(GVariant) reply = NULL; - g_autoptr(GUnixFDList) out_fd_list = NULL; - - reply = g_dbus_proxy_call_with_unix_fd_list_sync (G_DBUS_PROXY (self->proxy), - "GetProcFd", - g_variant_new ("(^ay)", path), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, - NULL, - &out_fd_list, - cancellable, - error); - - if (reply != NULL && out_fd_list != NULL) - { - gint handle = -1; - - g_variant_get (reply, "(h)", &handle); - - if (handle < g_unix_fd_list_get_length (out_fd_list)) - { - *out_fd = g_unix_fd_list_get (out_fd_list, handle, error); - return *out_fd != -1; - } - } - } - - if (!helpers_get_proc_fd (path, out_fd)) - return FALSE; - - g_clear_error (error); - return TRUE; -} - -static void -sysprof_helpers_get_proc_file_cb (IpcService *service, - GAsyncResult *result, - gpointer user_data) -{ - g_autoptr(GTask) task = user_data; - g_autoptr(GError) error = NULL; - g_autofree gchar *contents = NULL; - - g_assert (IPC_IS_SERVICE (service)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - if (!ipc_service_call_get_proc_file_finish (service, &contents, result, &error)) - { - const gchar *path = g_task_get_task_data (task); - gsize len; - - if (!helpers_get_proc_file (path, &contents, &len)) - { - g_task_return_error (task, g_steal_pointer (&error)); - return; - } - - g_clear_error (&error); - } - - g_task_return_pointer (task, g_steal_pointer (&contents), g_free); -} - -gboolean -sysprof_helpers_get_proc_file (SysprofHelpers *self, - const gchar *path, - GCancellable *cancellable, - gchar **contents, - GError **error) -{ - gsize len; - - g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE); - g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE); - - if (self->proxy != NULL) - { - if (ipc_service_call_get_proc_file_sync (self->proxy, path, contents, cancellable, error)) - return TRUE; - } - - if (!helpers_get_proc_file (path, contents, &len)) - return FALSE; - - if (error != NULL) - g_clear_error (error); - - return TRUE; -} - -void -sysprof_helpers_get_proc_file_async (SysprofHelpers *self, - const gchar *path, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GTask) task = NULL; - - g_return_if_fail (SYSPROF_IS_HELPERS (self)); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_helpers_get_proc_file_async); - g_task_set_task_data (task, g_strdup (path), g_free); - - if (!fail_if_no_proxy (self, task)) - ipc_service_call_get_proc_file (self->proxy, - path, - cancellable, - (GAsyncReadyCallback) sysprof_helpers_get_proc_file_cb, - g_steal_pointer (&task)); -} - -gboolean -sysprof_helpers_get_proc_file_finish (SysprofHelpers *self, - GAsyncResult *result, - gchar **contents, - GError **error) -{ - g_autofree gchar *ret = NULL; - - g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE); - g_return_val_if_fail (G_IS_TASK (result), FALSE); - - if ((ret = g_task_propagate_pointer (G_TASK (result), error))) - { - if (contents != NULL) - *contents = g_steal_pointer (&ret); - return TRUE; - } - - return FALSE; -} - -#ifdef __linux__ -static GVariant * -build_options_dict (struct perf_event_attr *attr) -{ - return g_variant_take_ref ( - g_variant_new_parsed ("[" - "{'comm', <%b>}," -#ifdef HAVE_PERF_CLOCKID - "{'clockid', <%i>}," - "{'use_clockid', <%b>}," -#endif - "{'config', <%t>}," - "{'disabled', <%b>}," - "{'exclude_idle', <%b>}," - "{'mmap', <%b>}," - "{'wakeup_events', <%u>}," - "{'sample_id_all', <%b>}," - "{'sample_period', <%t>}," - "{'sample_type', <%t>}," - "{'task', <%b>}," - "{'type', <%u>}" - "]", - (gboolean)!!attr->comm, -#ifdef HAVE_PERF_CLOCKID - (gint32)attr->clockid, - (gboolean)!!attr->use_clockid, -#endif - (guint64)attr->config, - (gboolean)!!attr->disabled, - (gboolean)!!attr->exclude_idle, - (gboolean)!!attr->mmap, - (guint32)attr->wakeup_events, - (gboolean)!!attr->sample_id_all, - (guint64)attr->sample_period, - (guint64)attr->sample_type, - (gboolean)!!attr->task, - (guint32)attr->type)); -} - -gboolean -sysprof_helpers_perf_event_open (SysprofHelpers *self, - struct perf_event_attr *attr, - gint32 pid, - gint32 cpu, - gint32 group_fd, - guint64 flags, - GCancellable *cancellable, - gint *out_fd, - GError **error) -{ - g_autoptr(GUnixFDList) fd_list = NULL; - g_autoptr(GUnixFDList) out_fd_list = NULL; - g_autoptr(GVariant) options = NULL; - g_autoptr(GVariant) reply = NULL; - gint handle = -1; - - g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE); - g_return_val_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable), FALSE); - g_return_val_if_fail (group_fd >= -1, FALSE); - g_return_val_if_fail (out_fd != NULL, FALSE); - - *out_fd = -1; - - if (self->proxy == NULL) - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_NOT_CONNECTED, - "No access to system proxy"); - return FALSE; - } - - if (group_fd != -1) - { - fd_list = g_unix_fd_list_new (); - handle = g_unix_fd_list_append (fd_list, group_fd, NULL); - } - - options = build_options_dict (attr); - - reply = g_dbus_proxy_call_with_unix_fd_list_sync (G_DBUS_PROXY (self->proxy), - "PerfEventOpen", - g_variant_new ("(@a{sv}iiht)", - options, - pid, - cpu, - handle, - flags), - G_DBUS_CALL_FLAGS_NONE, - -1, - fd_list, - &out_fd_list, - cancellable, - error); - - if (reply == NULL) - { - /* Try in-process (without elevated privs) */ - if (helpers_perf_event_open (options, pid, cpu, group_fd, flags, out_fd)) - { - g_clear_error (error); - return TRUE; - } - - return FALSE; - } - - if (out_fd_list == NULL || g_unix_fd_list_get_length (out_fd_list) != 1) - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - "Received invalid reply from peer"); - return FALSE; - } - - *out_fd = g_unix_fd_list_get (out_fd_list, 0, error); - - return *out_fd != -1; -} -#endif /* __linux__ */ - -static void -sysprof_helpers_authorize_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - g_autoptr(SysprofHelpers) self = user_data; - g_autoptr(GError) error = NULL; - - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (SYSPROF_IS_HELPERS (self)); - - if (!_sysprof_polkit_authorize_for_bus_finish (result, &error)) - { - while (self->auth_tasks.length > 0) - { - g_autoptr(GTask) task = g_queue_pop_head (&self->auth_tasks); - g_task_return_error (task, g_error_copy (error)); - } - } - else - { - self->did_auth = TRUE; - while (self->auth_tasks.length > 0) - { - g_autoptr(GTask) task = g_queue_pop_head (&self->auth_tasks); - g_task_return_boolean (task, TRUE); - } - } -} - -static void -sysprof_helpers_do_auth (SysprofHelpers *self) -{ - GDBusConnection *bus; - - g_assert (SYSPROF_IS_HELPERS (self)); - - if (self->proxy == NULL || self->did_auth) - { - /* No D-Bus/Polkit? Bail early, fail sooner. If we already successfully - * did auth, then short circuit to avoid spamming the user. - */ - while (self->auth_tasks.length > 0) - { - g_autoptr(GTask) task = g_queue_pop_head (&self->auth_tasks); - g_task_return_boolean (task, TRUE); - } - - return; - } - - bus = g_dbus_proxy_get_connection (G_DBUS_PROXY (self->proxy)); - - _sysprof_polkit_authorize_for_bus_async (bus, - "org.gnome.sysprof3.profile", - NULL, - TRUE, - NULL, - sysprof_helpers_authorize_cb, - g_object_ref (self)); -} - -void -sysprof_helpers_authorize_async (SysprofHelpers *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GTask) task = NULL; - - g_return_if_fail (SYSPROF_IS_HELPERS (self)); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_helpers_authorize_async); - - g_queue_push_tail (&self->auth_tasks, g_steal_pointer (&task)); - - if (self->auth_tasks.length == 1) - sysprof_helpers_do_auth (self); -} - -gboolean -sysprof_helpers_authorize_finish (SysprofHelpers *self, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE); - g_return_val_if_fail (G_IS_TASK (result), FALSE); - - return g_task_propagate_boolean (G_TASK (result), error); -} - -gboolean -sysprof_helpers_get_process_info (SysprofHelpers *self, - const gchar *attributes, - gboolean no_proxy, - GCancellable *cancellable, - GVariant **info, - GError **error) -{ - g_assert (SYSPROF_IS_HELPERS (self)); - g_assert (attributes != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - g_assert (info != NULL); - - if (no_proxy) - { - *info = helpers_get_process_info (attributes); - return TRUE; - } - - return ipc_service_call_get_process_info_sync (self->proxy, attributes, info, cancellable, error); -} - -static void -sysprof_helpers_get_process_info_cb (IpcService *service, - GAsyncResult *result, - gpointer user_data) -{ - g_autoptr(GTask) task = user_data; - g_autoptr(GVariant) info = NULL; - g_autoptr(GError) error = NULL; - - g_assert (IPC_IS_SERVICE (service)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - if (!ipc_service_call_get_process_info_finish (service, &info, result, &error)) - g_task_return_error (task, g_steal_pointer (&error)); - else - g_task_return_pointer (task, g_steal_pointer (&info), (GDestroyNotify)g_variant_unref); -} - -void -sysprof_helpers_get_process_info_async (SysprofHelpers *self, - const gchar *attributes, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GTask) task = NULL; - - g_assert (SYSPROF_IS_HELPERS (self)); - g_assert (attributes != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_helpers_get_process_info_async); - - ipc_service_call_get_process_info (self->proxy, - attributes, - cancellable, - (GAsyncReadyCallback) sysprof_helpers_get_process_info_cb, - g_steal_pointer (&task)); -} - -gboolean -sysprof_helpers_get_process_info_finish (SysprofHelpers *self, - GAsyncResult *result, - GVariant **info, - GError **error) -{ - g_autoptr(GVariant) ret = NULL; - - g_assert (SYSPROF_IS_HELPERS (self)); - g_assert (G_IS_TASK (result)); - - if ((ret = g_task_propagate_pointer (G_TASK (result), error))) - { - if (info) - *info = g_steal_pointer (&ret); - return TRUE; - } - - return FALSE; -} - -static void -sysprof_helpers_set_governor_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - IpcService *proxy = (IpcService *)object; - g_autoptr(GError) error = NULL; - g_autoptr(GTask) task = user_data; - gchar *old_governor = NULL; - - g_assert (IPC_IS_SERVICE (proxy)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - if (!ipc_service_call_set_governor_finish (proxy, &old_governor, result, &error)) - g_task_return_error (task, g_steal_pointer (&error)); - else - g_task_return_pointer (task, old_governor, g_free); -} - -void -sysprof_helpers_set_governor_async (SysprofHelpers *self, - const gchar *governor, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GTask) task = NULL; - - g_return_if_fail (SYSPROF_IS_HELPERS (self)); - g_return_if_fail (governor != NULL); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_helpers_set_governor_async); - - if (fail_if_no_proxy (self, task)) - return; - - ipc_service_call_set_governor (self->proxy, - governor, - cancellable, - sysprof_helpers_set_governor_cb, - g_steal_pointer (&task)); -} - -gboolean -sysprof_helpers_set_governor_finish (SysprofHelpers *self, - GAsyncResult *result, - gchar **old_governor, - GError **error) -{ - g_autofree gchar *ret = NULL; - - g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE); - g_return_val_if_fail (G_IS_TASK (result), FALSE); - - if ((ret = g_task_propagate_pointer (G_TASK (result), error))) - { - if (old_governor != NULL) - *old_governor = g_steal_pointer (&ret); - return TRUE; - } - - return FALSE; -} - -static void -sysprof_helpers_set_paranoid_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - IpcService *proxy = (IpcService *)object; - g_autoptr(GError) error = NULL; - g_autoptr(GTask) task = user_data; - int old_paranoid = G_MAXINT; - - g_assert (IPC_IS_SERVICE (proxy)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - if (!ipc_service_call_set_paranoid_finish (proxy, &old_paranoid, result, &error)) - g_task_return_error (task, g_steal_pointer (&error)); - else - g_task_return_int (task, old_paranoid); -} - -void -sysprof_helpers_set_paranoid_async (SysprofHelpers *self, - int paranoid, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GTask) task = NULL; - - g_return_if_fail (SYSPROF_IS_HELPERS (self)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_helpers_set_paranoid_async); - - if (fail_if_no_proxy (self, task)) - return; - - ipc_service_call_set_paranoid (self->proxy, - paranoid, - cancellable, - sysprof_helpers_set_paranoid_cb, - g_steal_pointer (&task)); -} - -gboolean -sysprof_helpers_set_paranoid_finish (SysprofHelpers *self, - GAsyncResult *result, - int *old_paranoid, - GError **error) -{ - g_autoptr(GError) local_error = NULL; - - g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE); - g_return_val_if_fail (G_IS_TASK (result), FALSE); - - *old_paranoid = g_task_propagate_int (G_TASK (result), &local_error); - - if (local_error) - { - g_propagate_error (error, g_steal_pointer (&local_error)); - return FALSE; - } - - return TRUE; -} diff --git a/src/libsysprof/sysprof-helpers.h b/src/libsysprof/sysprof-helpers.h deleted file mode 100644 index b0794bbc..00000000 --- a/src/libsysprof/sysprof-helpers.h +++ /dev/null @@ -1,121 +0,0 @@ -/* sysprof-helpers.h - * - * Copyright 2019 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 - */ - -#pragma once - -#include - -#ifdef __linux__ -# include -#endif - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_HELPERS (sysprof_helpers_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofHelpers, sysprof_helpers, SYSPROF, HELPERS, GObject) - -SysprofHelpers *sysprof_helpers_get_default (void); -void sysprof_helpers_authorize_async (SysprofHelpers *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean sysprof_helpers_authorize_finish (SysprofHelpers *self, - GAsyncResult *result, - GError **error); -gboolean sysprof_helpers_list_processes (SysprofHelpers *self, - GCancellable *cancellable, - gint32 **processes, - gsize *n_processes, - GError **error); -void sysprof_helpers_list_processes_async (SysprofHelpers *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean sysprof_helpers_list_processes_finish (SysprofHelpers *self, - GAsyncResult *result, - gint32 **processes, - gsize *n_processes, - GError **error); -gboolean sysprof_helpers_get_proc_fd (SysprofHelpers *self, - const gchar *path, - GCancellable *cancellable, - gint *out_fd, - GError **error); -gboolean sysprof_helpers_get_proc_file (SysprofHelpers *self, - const gchar *path, - GCancellable *cancellable, - gchar **contents, - GError **error); -void sysprof_helpers_get_proc_file_async (SysprofHelpers *self, - const gchar *path, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean sysprof_helpers_get_proc_file_finish (SysprofHelpers *self, - GAsyncResult *result, - gchar **contents, - GError **error); -gboolean sysprof_helpers_get_process_info (SysprofHelpers *self, - const gchar *attributes, - gboolean no_proxy, - GCancellable *cancellable, - GVariant **info, - GError **error); -void sysprof_helpers_get_process_info_async (SysprofHelpers *self, - const gchar *attributes, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean sysprof_helpers_get_process_info_finish(SysprofHelpers *self, - GAsyncResult *result, - GVariant **info, - GError **error); -#ifdef __linux__ -gboolean sysprof_helpers_perf_event_open (SysprofHelpers *self, - struct perf_event_attr *attr, - gint32 pid, - gint32 cpu, - gint32 group_fd, - guint64 flags, - GCancellable *cancellable, - gint *out_fd, - GError **error); -#endif -void sysprof_helpers_set_governor_async (SysprofHelpers *self, - const gchar *governor, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean sysprof_helpers_set_governor_finish (SysprofHelpers *self, - GAsyncResult *result, - gchar **old_governor, - GError **error); -void sysprof_helpers_set_paranoid_async (SysprofHelpers *self, - int paranoid, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean sysprof_helpers_set_paranoid_finish (SysprofHelpers *self, - GAsyncResult *result, - int *old_paranoid, - GError **error); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-hostinfo-source.c b/src/libsysprof/sysprof-hostinfo-source.c deleted file mode 100644 index 5a7f8991..00000000 --- a/src/libsysprof/sysprof-hostinfo-source.c +++ /dev/null @@ -1,496 +0,0 @@ -/* sysprof-hostinfo-source.c - * - * Copyright 2016-2019 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 - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sysprof-helpers.h" -#include "sysprof-hostinfo-source.h" - -#define PROC_STAT_BUF_SIZE 4096 - -struct _SysprofHostinfoSource -{ - GObject parent_instance; - - guint handler; - gint n_cpu; - gint stat_fd; - guint combined_id; - - GArray *freqs; - - SysprofCaptureWriter *writer; - GArray *cpu_info; - gchar *stat_buf; -}; - -typedef struct -{ - gint counter_base; - gdouble total; - glong last_user; - glong last_idle; - glong last_system; - glong last_nice; - glong last_iowait; - glong last_irq; - glong last_softirq; - glong last_steal; - glong last_guest; - glong last_guest_nice; -} CpuInfo; - -typedef struct -{ - gint stat_fd; - gint64 max; -} CpuFreq; - -static void source_iface_init (SysprofSourceInterface *iface); - -G_DEFINE_TYPE_EXTENDED (SysprofHostinfoSource, sysprof_hostinfo_source, G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init)) - -SysprofSource * -sysprof_hostinfo_source_new (void) -{ - return g_object_new (SYSPROF_TYPE_HOSTINFO_SOURCE, NULL); -} - -static gboolean -read_stat (SysprofHostinfoSource *self) -{ - gssize len; - - g_assert (self != NULL); - g_assert (self->stat_fd != -1); - g_assert (self->stat_buf != NULL); - - if (lseek (self->stat_fd, 0, SEEK_SET) != 0) - return FALSE; - - len = read (self->stat_fd, self->stat_buf, PROC_STAT_BUF_SIZE); - if (len <= 0) - return FALSE; - - if (len < PROC_STAT_BUF_SIZE) - self->stat_buf[len] = 0; - else - self->stat_buf[PROC_STAT_BUF_SIZE-1] = 0; - - return TRUE; -} - -static void -poll_cpu (SysprofHostinfoSource *self) -{ - gchar cpu[64] = { 0 }; - glong user; - glong sys; - glong nice; - glong idle; - glong iowait; - glong irq; - glong softirq; - glong steal; - glong guest; - glong guest_nice; - glong user_calc; - glong system_calc; - glong nice_calc; - glong idle_calc; - glong iowait_calc; - glong irq_calc; - glong softirq_calc; - glong steal_calc; - glong guest_calc; - glong guest_nice_calc; - glong total; - gchar *line; - gint ret; - gint id; - - if (read_stat (self)) - { - line = self->stat_buf; - - for (gsize i = 0; self->stat_buf[i]; i++) - { - if (self->stat_buf[i] == '\n') - { - self->stat_buf[i] = '\0'; - - if (strncmp (line, "cpu", 3) == 0) - { - if (isdigit (line[3])) - { - CpuInfo *cpu_info; - - user = nice = sys = idle = id = 0; - ret = sscanf (line, "%s %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld", - cpu, &user, &nice, &sys, &idle, - &iowait, &irq, &softirq, &steal, &guest, &guest_nice); - if (ret != 11) - goto next; - - ret = sscanf(cpu, "cpu%d", &id); - - if (ret != 1 || id < 0 || id >= self->n_cpu) - goto next; - - cpu_info = &g_array_index (self->cpu_info, CpuInfo, id); - - user_calc = user - cpu_info->last_user; - nice_calc = nice - cpu_info->last_nice; - system_calc = sys - cpu_info->last_system; - idle_calc = idle - cpu_info->last_idle; - iowait_calc = iowait - cpu_info->last_iowait; - irq_calc = irq - cpu_info->last_irq; - softirq_calc = softirq - cpu_info->last_softirq; - steal_calc = steal - cpu_info->last_steal; - guest_calc = guest - cpu_info->last_guest; - guest_nice_calc = guest_nice - cpu_info->last_guest_nice; - - total = user_calc + nice_calc + system_calc + idle_calc + iowait_calc + irq_calc + softirq_calc + steal_calc + guest_calc + guest_nice_calc; - cpu_info->total = ((total - idle_calc) / (gdouble)total) * 100.0; - - cpu_info->last_user = user; - cpu_info->last_nice = nice; - cpu_info->last_idle = idle; - cpu_info->last_system = sys; - cpu_info->last_iowait = iowait; - cpu_info->last_irq = irq; - cpu_info->last_softirq = softirq; - cpu_info->last_steal = steal; - cpu_info->last_guest = guest; - cpu_info->last_guest_nice = guest_nice; - } - } - else - { - /* CPU info comes first. Skip further lines. */ - break; - } - - next: - line = &self->stat_buf[i + 1]; - } - } - } -} - -static gdouble -get_cpu_freq (SysprofHostinfoSource *self, - guint cpu) -{ - const CpuFreq *freq; - - g_assert (SYSPROF_IS_HOSTINFO_SOURCE (self)); - g_assert (cpu < self->freqs->len); - - freq = &g_array_index (self->freqs, CpuFreq, cpu); - - if (freq->stat_fd > -1) - { - gchar buf[128]; - gssize len; - - lseek (freq->stat_fd, 0, SEEK_SET); - len = read (freq->stat_fd, buf, sizeof buf - 1); - - if (len > 0 && len < sizeof buf) - { - gint64 val; - - buf[len] = 0; - g_strstrip (buf); - val = g_ascii_strtoll (buf, NULL, 10); - - return (gdouble)val / (gdouble)freq->max * 100.0; - } - } - - return 0.0; -} - -static void -publish_cpu (SysprofHostinfoSource *self) -{ - SysprofCaptureCounterValue *counter_values; - guint *counter_ids; - glong total_usage = 0; - - counter_ids = alloca (sizeof *counter_ids * (self->n_cpu * 2 + 1)); - counter_values = alloca (sizeof *counter_values * (self->n_cpu * 2 + 1)); - - for (guint i = 0; i < self->n_cpu; i++) - { - CpuInfo *info = &g_array_index (self->cpu_info, CpuInfo, i); - SysprofCaptureCounterValue *value = &counter_values[i*2]; - guint *id = &counter_ids[i*2]; - - *id = info->counter_base; - value->vdbl = info->total; - - id++; - value++; - - *id = info->counter_base + 1; - value->vdbl = get_cpu_freq (self, i); - - total_usage += info->total; - } - - /* Add combined counter */ - counter_ids[self->n_cpu * 2] = self->combined_id; - counter_values[self->n_cpu * 2].vdbl = total_usage / (gdouble)self->n_cpu; - - sysprof_capture_writer_set_counters (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - -1, - counter_ids, - counter_values, - self->n_cpu * 2 + 1); -} - -static gboolean -collect_hostinfo_cb (gpointer data) -{ - SysprofHostinfoSource *self = data; - - g_assert (SYSPROF_IS_HOSTINFO_SOURCE (self)); - - poll_cpu (self); - publish_cpu (self); - - return G_SOURCE_CONTINUE; -} - -static void -sysprof_hostinfo_source_finalize (GObject *object) -{ - SysprofHostinfoSource *self = (SysprofHostinfoSource *)object; - - if (self->handler) - { - g_source_remove (self->handler); - self->handler = 0; - } - - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); - g_clear_pointer (&self->cpu_info, g_array_unref); - g_clear_pointer (&self->stat_buf, g_free); - g_clear_pointer (&self->freqs, g_array_unref); - - G_OBJECT_CLASS (sysprof_hostinfo_source_parent_class)->finalize (object); -} - -static void -sysprof_hostinfo_source_class_init (SysprofHostinfoSourceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_hostinfo_source_finalize; -} - -static void -sysprof_hostinfo_source_init (SysprofHostinfoSource *self) -{ - self->stat_fd = -1; - self->cpu_info = g_array_new (FALSE, TRUE, sizeof (CpuInfo)); - self->stat_buf = g_malloc (PROC_STAT_BUF_SIZE); - self->freqs = g_array_new (FALSE, FALSE, sizeof (CpuFreq)); -} - -static void -sysprof_hostinfo_source_set_writer (SysprofSource *source, - SysprofCaptureWriter *writer) -{ - SysprofHostinfoSource *self = (SysprofHostinfoSource *)source; - - g_assert (SYSPROF_IS_HOSTINFO_SOURCE (self)); - g_assert (writer != NULL); - - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); - self->writer = sysprof_capture_writer_ref (writer); -} - -static void -sysprof_hostinfo_source_start (SysprofSource *source) -{ - SysprofHostinfoSource *self = (SysprofHostinfoSource *)source; - - g_assert (SYSPROF_IS_HOSTINFO_SOURCE (self)); - - /* 20 samples per second */ - self->handler = g_timeout_add (1000/20, collect_hostinfo_cb, self); -} - -static void -sysprof_hostinfo_source_stop (SysprofSource *source) -{ - SysprofHostinfoSource *self = (SysprofHostinfoSource *)source; - - g_assert (SYSPROF_IS_HOSTINFO_SOURCE (self)); - - g_source_remove (self->handler); - self->handler = 0; - - if (self->stat_fd != -1) - { - close (self->stat_fd); - self->stat_fd = -1; - } - - for (guint i = 0; i < self->freqs->len; i++) - { - CpuFreq *freq = &g_array_index (self->freqs, CpuFreq, i); - - if (freq->stat_fd != -1) - close (freq->stat_fd); - } - - if (self->freqs->len > 0) - g_array_remove_range (self->freqs, 0, self->freqs->len); - - sysprof_source_emit_finished (SYSPROF_SOURCE (self)); -} - -static void -sysprof_hostinfo_source_prepare (SysprofSource *source) -{ - SysprofHostinfoSource *self = (SysprofHostinfoSource *)source; - SysprofCaptureCounter *counters; - SysprofCaptureCounter *combined; - gint cpuinfo_fd; - - g_assert (SYSPROF_IS_HOSTINFO_SOURCE (self)); - g_assert (self->writer != NULL); - - /* We can generally get this even in containers */ - if (-1 != (cpuinfo_fd = g_open ("/proc/cpuinfo", O_RDONLY))) - { - sysprof_capture_writer_add_file_fd (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - -1, - "/proc/cpuinfo", - cpuinfo_fd); - close (cpuinfo_fd); - } - - self->stat_fd = open ("/proc/stat", O_RDONLY); - self->n_cpu = g_get_num_processors (); - - g_array_set_size (self->cpu_info, 0); - - counters = alloca (sizeof *counters * (self->n_cpu * 2 + 1)); - - for (guint i = 0; i < self->n_cpu; i++) - { - g_autofree gchar *max_path = NULL; - g_autofree gchar *cur_path = NULL; - g_autofree gchar *maxstr = NULL; - SysprofCaptureCounter *ctr = &counters[i*2]; - CpuInfo info = { 0 }; - CpuFreq freq = { 0 }; - - /* - * Request 2 counter values. - * One for CPU and one for Frequency. - */ - info.counter_base = sysprof_capture_writer_request_counter (self->writer, 2); - - /* - * Define counters for capture file. - */ - ctr->id = info.counter_base; - ctr->type = SYSPROF_CAPTURE_COUNTER_DOUBLE; - ctr->value.vdbl = 0; - g_strlcpy (ctr->category, "CPU Percent", sizeof ctr->category); - g_snprintf (ctr->name, sizeof ctr->name, "Total CPU %d", i); - g_snprintf (ctr->description, sizeof ctr->description, - "Total CPU usage %d", i); - - ctr++; - - max_path = g_strdup_printf ("/sys/devices/system/cpu/cpu%u/cpufreq/scaling_max_freq", i); - cur_path = g_strdup_printf ("/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", i); - - if (g_file_get_contents (max_path, &maxstr, NULL, NULL)) - { - g_strstrip (maxstr); - freq.max = g_ascii_strtoll (maxstr, NULL, 10); - } - - freq.stat_fd = -1; - sysprof_helpers_get_proc_fd (sysprof_helpers_get_default (), - cur_path, NULL, &freq.stat_fd, NULL); - g_array_append_val (self->freqs, freq); - - ctr->id = info.counter_base + 1; - ctr->type = SYSPROF_CAPTURE_COUNTER_DOUBLE; - ctr->value.vdbl = 0; - g_strlcpy (ctr->category, "CPU Frequency", sizeof ctr->category); - g_snprintf (ctr->name, sizeof ctr->name, "CPU %d", i); - g_snprintf (ctr->description, sizeof ctr->description, - "Frequency of CPU %d", i); - - g_array_append_val (self->cpu_info, info); - } - - /* Now add combined counter */ - self->combined_id = sysprof_capture_writer_request_counter (self->writer, 1); - combined = &counters[self->n_cpu * 2]; - combined->id = self->combined_id; - combined->type = SYSPROF_CAPTURE_COUNTER_DOUBLE; - combined->value.vdbl = 0; - g_strlcpy (combined->category, "CPU Percent", sizeof combined->category); - g_snprintf (combined->name, sizeof combined->name, "Combined"); - g_snprintf (combined->description, sizeof combined->description, "Combined CPU usage"); - - sysprof_capture_writer_define_counters (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - -1, - counters, - self->n_cpu * 2 + 1); - - sysprof_source_emit_ready (SYSPROF_SOURCE (self)); -} - -static void -source_iface_init (SysprofSourceInterface *iface) -{ - iface->set_writer = sysprof_hostinfo_source_set_writer; - iface->prepare = sysprof_hostinfo_source_prepare; - iface->start = sysprof_hostinfo_source_start; - iface->stop = sysprof_hostinfo_source_stop; -} diff --git a/src/libsysprof/sysprof-hostinfo-source.h b/src/libsysprof/sysprof-hostinfo-source.h deleted file mode 100644 index 09d21d61..00000000 --- a/src/libsysprof/sysprof-hostinfo-source.h +++ /dev/null @@ -1,39 +0,0 @@ -/* sysprof-hostinfo-source.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include "sysprof-source.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_HOSTINFO_SOURCE (sysprof_hostinfo_source_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofHostinfoSource, sysprof_hostinfo_source, SYSPROF, HOSTINFO_SOURCE, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSource *sysprof_hostinfo_source_new (void); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-jitmap-symbol-resolver.c b/src/libsysprof/sysprof-jitmap-symbol-resolver.c deleted file mode 100644 index 2015eb50..00000000 --- a/src/libsysprof/sysprof-jitmap-symbol-resolver.c +++ /dev/null @@ -1,125 +0,0 @@ -/* sysprof-jitmap-symbol-resolver.c - * - * Copyright 2016-2019 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 - */ - -#include "config.h" - -#include "sysprof-kernel-symbol.h" -#include "sysprof-jitmap-symbol-resolver.h" - -struct _SysprofJitmapSymbolResolver -{ - GObject parent_instance; - GHashTable *jitmap; -}; - -static void symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface); - -G_DEFINE_TYPE_EXTENDED (SysprofJitmapSymbolResolver, - sysprof_jitmap_symbol_resolver, - G_TYPE_OBJECT, - 0, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SYMBOL_RESOLVER, - symbol_resolver_iface_init)) - -static void -sysprof_jitmap_symbol_resolver_finalize (GObject *object) -{ - SysprofJitmapSymbolResolver *self = (SysprofJitmapSymbolResolver *)object; - - g_clear_pointer (&self->jitmap, g_hash_table_unref); - - G_OBJECT_CLASS (sysprof_jitmap_symbol_resolver_parent_class)->finalize (object); -} - -static void -sysprof_jitmap_symbol_resolver_class_init (SysprofJitmapSymbolResolverClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_jitmap_symbol_resolver_finalize; -} - -static void -sysprof_jitmap_symbol_resolver_init (SysprofJitmapSymbolResolver *self) -{ - self->jitmap = g_hash_table_new_full (NULL, NULL, NULL, g_free); -} - -static void -sysprof_jitmap_symbol_resolver_load (SysprofSymbolResolver *resolver, - SysprofCaptureReader *reader) -{ - SysprofJitmapSymbolResolver *self = (SysprofJitmapSymbolResolver *)resolver; - SysprofCaptureFrameType type; - - g_assert (SYSPROF_IS_JITMAP_SYMBOL_RESOLVER (self)); - g_assert (reader != NULL); - - while (sysprof_capture_reader_peek_type (reader, &type)) - { - const SysprofCaptureJitmap *jitmap; - SysprofCaptureJitmapIter iter; - SysprofCaptureAddress addr; - const gchar *str; - - if (type != SYSPROF_CAPTURE_FRAME_JITMAP) - { - if (!sysprof_capture_reader_skip (reader)) - return; - continue; - } - - if (!(jitmap = sysprof_capture_reader_read_jitmap (reader))) - return; - - sysprof_capture_jitmap_iter_init (&iter, jitmap); - while (sysprof_capture_jitmap_iter_next (&iter, &addr, &str)) - g_hash_table_insert (self->jitmap, GSIZE_TO_POINTER (addr), g_strdup (str)); - } -} - -static gchar * -sysprof_jitmap_symbol_resolver_resolve (SysprofSymbolResolver *resolver, - guint64 time, - GPid pid, - SysprofCaptureAddress address, - GQuark *tag) -{ - SysprofJitmapSymbolResolver *self = (SysprofJitmapSymbolResolver *)resolver; - - g_assert (SYSPROF_IS_JITMAP_SYMBOL_RESOLVER (self)); - - *tag = 0; - - return g_strdup (g_hash_table_lookup (self->jitmap, GSIZE_TO_POINTER (address))); -} - -static void -symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface) -{ - iface->load = sysprof_jitmap_symbol_resolver_load; - iface->resolve = sysprof_jitmap_symbol_resolver_resolve; -} - -SysprofSymbolResolver * -sysprof_jitmap_symbol_resolver_new (void) -{ - return g_object_new (SYSPROF_TYPE_JITMAP_SYMBOL_RESOLVER, NULL); -} diff --git a/src/libsysprof/sysprof-jitmap-symbol-resolver.h b/src/libsysprof/sysprof-jitmap-symbol-resolver.h deleted file mode 100644 index 0d366194..00000000 --- a/src/libsysprof/sysprof-jitmap-symbol-resolver.h +++ /dev/null @@ -1,38 +0,0 @@ -/* sysprof-jitmap-symbol-resolver.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include "sysprof-symbol-resolver.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_JITMAP_SYMBOL_RESOLVER (sysprof_jitmap_symbol_resolver_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofJitmapSymbolResolver, sysprof_jitmap_symbol_resolver, SYSPROF, JITMAP_SYMBOL_RESOLVER, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSymbolResolver *sysprof_jitmap_symbol_resolver_new (void); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-kallsyms.c b/src/libsysprof/sysprof-kallsyms.c deleted file mode 100644 index 4545fba0..00000000 --- a/src/libsysprof/sysprof-kallsyms.c +++ /dev/null @@ -1,168 +0,0 @@ -/* sysprof-kallsyms.c - * - * Copyright 2018-2019 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 G_LOG_DOMAIN "sysprof-kallsyms" - -#include "config.h" - -#include -#include -#include - -#include "sysprof-kallsyms.h" - -struct _SysprofKallsyms -{ - gchar *buf; - gsize buflen; - gchar *endptr; - gchar *iter; -}; - -void -sysprof_kallsyms_free (SysprofKallsyms *self) -{ - if (self != NULL) - { - g_clear_pointer (&self->buf, g_free); - g_slice_free (SysprofKallsyms, self); - } -} - -SysprofKallsyms * -sysprof_kallsyms_new_take (gchar *data) -{ - g_autoptr(SysprofKallsyms) self = NULL; - - self = g_slice_new0 (SysprofKallsyms); - self->buf = g_steal_pointer (&data); - self->buflen = strlen (self->buf); - self->endptr = self->buf + self->buflen; - self->iter = self->buf; - - return g_steal_pointer (&self); -} - -SysprofKallsyms * -sysprof_kallsyms_new (const gchar *path) -{ - g_autoptr(SysprofKallsyms) self = NULL; - - if (path == NULL) - path = "/proc/kallsyms"; - - self = g_slice_new0 (SysprofKallsyms); - - if (!g_file_get_contents (path, &self->buf, &self->buflen, NULL)) - return NULL; - - self->iter = self->buf; - self->endptr = self->buf + self->buflen; - - return g_steal_pointer (&self); -} - -gboolean -sysprof_kallsyms_next (SysprofKallsyms *self, - const gchar **name, - guint64 *address, - guint8 *type) -{ - guint64 addr; - char *tok; - char *pptr; - - g_return_val_if_fail (self != NULL, FALSE); - g_return_val_if_fail (self->buf != NULL, FALSE); - g_return_val_if_fail (self->buflen > 0, FALSE); - g_return_val_if_fail (self->iter != NULL, FALSE); - g_return_val_if_fail (self->endptr != NULL, FALSE); - -try_next: - - if (self->iter >= self->endptr) - return FALSE; - - tok = strtok_r (self->iter, " \t\n", &self->iter); - if (!tok || *tok == 0) - return FALSE; - - if (*tok == '[') - { - tok = strtok_r (self->iter, " \t\n", &self->iter); - if (!tok || *tok == 0) - return FALSE; - } - - /* We'll keep going if we fail to parse, (null) usually, so that we - * just skip to the next line. - */ - addr = g_ascii_strtoull (tok, &pptr, 16); - if ((pptr == tok) || (addr == G_MAXUINT64 && errno == ERANGE) || (addr == 0 && errno == EINVAL)) - addr = 0; - - *address = addr; - - if (self->iter >= self->endptr) - return FALSE; - - tok = strtok_r (self->iter, " \t\n", &self->iter); - if (!tok || *tok == 0) - return FALSE; - - switch (*tok) - { - case 'A': - case 'B': - case 'D': - case 'R': - case 'T': - case 'V': - case 'W': - case 'a': - case 'b': - case 'd': - case 'r': - case 't': - case 'w': - *type = *tok; - break; - - default: - return FALSE; - } - - if (self->iter >= self->endptr) - return FALSE; - - tok = strtok_r (self->iter, " \t\n", &self->iter); - if (!tok || *tok == 0) - return FALSE; - - if (self->iter >= self->endptr) - return FALSE; - - if (addr == 0) - goto try_next; - - *name = tok; - - return TRUE; -} diff --git a/src/libsysprof/sysprof-kallsyms.h b/src/libsysprof/sysprof-kallsyms.h deleted file mode 100644 index c50303ea..00000000 --- a/src/libsysprof/sysprof-kallsyms.h +++ /dev/null @@ -1,39 +0,0 @@ -/* sysprof-kallsyms.h - * - * Copyright 2018-2019 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 - */ - -#pragma once - -#include - -G_BEGIN_DECLS - -typedef struct _SysprofKallsyms SysprofKallsyms; - -SysprofKallsyms *sysprof_kallsyms_new (const gchar *path); -SysprofKallsyms *sysprof_kallsyms_new_take (gchar *data); -gboolean sysprof_kallsyms_next (SysprofKallsyms *self, - const gchar **name, - guint64 *address, - guint8 *type); -void sysprof_kallsyms_free (SysprofKallsyms *self); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofKallsyms, sysprof_kallsyms_free) - -G_END_DECLS diff --git a/src/libsysprof/sysprof-kernel-symbol-resolver.c b/src/libsysprof/sysprof-kernel-symbol-resolver.c deleted file mode 100644 index 6e754297..00000000 --- a/src/libsysprof/sysprof-kernel-symbol-resolver.c +++ /dev/null @@ -1,156 +0,0 @@ -/* sysprof-kernel-symbol-resolver.c - * - * Copyright 2016-2019 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 G_LOG_DOMAIN "sysprof-kernel-symbol-resolver" - -#include "config.h" - -#include - -#include "sysprof-kallsyms.h" -#include "sysprof-kernel-symbol.h" -#include "sysprof-kernel-symbol-resolver.h" -#include "sysprof-private.h" - -#include "sysprof-platform.h" - -struct _SysprofKernelSymbolResolver -{ - GObject parent_instance; - SysprofKernelSymbols *symbols; -}; - -static GQuark linux_quark; - -static gchar * -sysprof_kernel_symbol_resolver_resolve_with_context (SysprofSymbolResolver *resolver, - guint64 time, - GPid pid, - SysprofAddressContext context, - SysprofCaptureAddress address, - GQuark *tag) -{ - SysprofKernelSymbolResolver *self = (SysprofKernelSymbolResolver *)resolver; - const SysprofKernelSymbol *sym; - - g_assert (SYSPROF_IS_SYMBOL_RESOLVER (self)); - g_assert (tag != NULL); - - if (context != SYSPROF_ADDRESS_CONTEXT_KERNEL) - return NULL; - - if (self->symbols == NULL) - return NULL; - - if ((sym = _sysprof_kernel_symbols_lookup (self->symbols, address))) - { - *tag = linux_quark; - return g_strdup (sym->name); - } - - return NULL; -} - -static void -sysprof_kernel_symbol_resolver_load (SysprofSymbolResolver *resolver, - SysprofCaptureReader *reader) -{ - static const guint8 zero[] = {0}; - SysprofKernelSymbolResolver *self = (SysprofKernelSymbolResolver *)resolver; - g_autoptr(GByteArray) bytes = NULL; - g_autoptr(SysprofKallsyms) kallsyms = NULL; - guint8 buf[4096]; - gint data_fd; - - g_assert (SYSPROF_IS_KERNEL_SYMBOL_RESOLVER (self)); - g_assert (reader != NULL); - - /* If there is an embedded __symbols__ file, then we won't do anything - * because we want to use the symbols from the peer. - */ - if (sysprof_capture_reader_find_file (reader, "__symbols__")) - return; - - sysprof_capture_reader_reset (reader); - - if (-1 == (data_fd = sysprof_memfd_create ("[sysprof-kallsyms]")) || - !sysprof_capture_reader_read_file_fd (reader, "/proc/kallsyms", data_fd)) - { - if (data_fd != -1) - close (data_fd); - self->symbols = _sysprof_kernel_symbols_get_shared (); - return; - } - - bytes = g_byte_array_new (); - lseek (data_fd, 0, SEEK_SET); - - for (;;) - { - gssize len = read (data_fd, buf, sizeof buf); - - if (len <= 0) - break; - - g_byte_array_append (bytes, buf, len); - } - - g_byte_array_append (bytes, zero, 1); - - if (bytes->len > 1) - { - kallsyms = sysprof_kallsyms_new_take ((gchar *)g_byte_array_free (g_steal_pointer (&bytes), FALSE)); - self->symbols = _sysprof_kernel_symbols_new_from_kallsyms (kallsyms); - } - else - { - self->symbols = _sysprof_kernel_symbols_get_shared (); - } -} - -static void -symbol_resolver_iface_init (SysprofSymbolResolverInterface *iface) -{ - iface->load = sysprof_kernel_symbol_resolver_load; - iface->resolve_with_context = sysprof_kernel_symbol_resolver_resolve_with_context; -} - -G_DEFINE_TYPE_WITH_CODE (SysprofKernelSymbolResolver, - sysprof_kernel_symbol_resolver, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SYMBOL_RESOLVER, - symbol_resolver_iface_init)) - -static void -sysprof_kernel_symbol_resolver_class_init (SysprofKernelSymbolResolverClass *klass) -{ - linux_quark = g_quark_from_static_string ("Kernel"); -} - -static void -sysprof_kernel_symbol_resolver_init (SysprofKernelSymbolResolver *skernel) -{ -} - -SysprofSymbolResolver * -sysprof_kernel_symbol_resolver_new (void) -{ - return g_object_new (SYSPROF_TYPE_KERNEL_SYMBOL_RESOLVER, NULL); -} diff --git a/src/libsysprof/sysprof-kernel-symbol-resolver.h b/src/libsysprof/sysprof-kernel-symbol-resolver.h deleted file mode 100644 index 563e26f0..00000000 --- a/src/libsysprof/sysprof-kernel-symbol-resolver.h +++ /dev/null @@ -1,38 +0,0 @@ -/* sysprof-kernel-symbol-resolver.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include "sysprof-symbol-resolver.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_KERNEL_SYMBOL_RESOLVER (sysprof_kernel_symbol_resolver_get_type()) - -G_DECLARE_FINAL_TYPE (SysprofKernelSymbolResolver, sysprof_kernel_symbol_resolver, SYSPROF, KERNEL_SYMBOL_RESOLVER, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSymbolResolver *sysprof_kernel_symbol_resolver_new (void); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-kernel-symbol.c b/src/libsysprof/sysprof-kernel-symbol.c deleted file mode 100644 index a474cdd9..00000000 --- a/src/libsysprof/sysprof-kernel-symbol.c +++ /dev/null @@ -1,252 +0,0 @@ -/* sysprof-kernel-symbol.c - * - * Copyright 2016-2019 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 G_LOG_DOMAIN "sysprof-kernel-symbol" - -#include "config.h" - -#include -#include - -#include "sysprof-helpers.h" -#include "sysprof-kallsyms.h" -#include "sysprof-kernel-symbol.h" -#include "sysprof-private.h" - -static G_LOCK_DEFINE (kernel_lock); -static GStringChunk *kernel_symbol_strs; -static GHashTable *kernel_symbols_skip_hash; -static const gchar *kernel_symbols_skip[] = { - /* IRQ stack */ - "common_interrupt", - "apic_timer_interrupt", - "smp_apic_timer_interrupt", - "hrtimer_interrupt", - "__run_hrtimer", - "perf_swevent_hrtimer", - "perf_event_overflow", - "__perf_event_overflow", - "perf_prepare_sample", - "perf_callchain", - "perf_swcounter_hrtimer", - "perf_counter_overflow", - "__perf_counter_overflow", - "perf_counter_output", - - /* NMI stack */ - "nmi_stack_correct", - "do_nmi", - "notify_die", - "atomic_notifier_call_chain", - "notifier_call_chain", - "perf_event_nmi_handler", - "perf_counter_nmi_handler", - "intel_pmu_handle_irq", - "perf_event_overflow", - "perf_counter_overflow", - "__perf_event_overflow", - "perf_prepare_sample", - "perf_callchain", -}; - -static inline gboolean -type_is_ignored (guint8 type) -{ - /* Only allow symbols in the text (code) section */ - return (type != 't' && type != 'T'); -} - -static gint -sysprof_kernel_symbol_compare (gconstpointer a, - gconstpointer b) -{ - const SysprofKernelSymbol *syma = a; - const SysprofKernelSymbol *symb = b; - - if (syma->address > symb->address) - return 1; - else if (syma->address == symb->address) - return 0; - else - return -1; -} - -static void -do_shared_init (void) -{ - static gsize once; - - if (g_once_init_enter (&once)) - { - g_autoptr(GHashTable) skip = NULL; - - kernel_symbol_strs = g_string_chunk_new (4096 * 4); - - skip = g_hash_table_new (g_str_hash, g_str_equal); - for (guint i = 0; i < G_N_ELEMENTS (kernel_symbols_skip); i++) - g_hash_table_insert (skip, (gchar *)kernel_symbols_skip[i], NULL); - kernel_symbols_skip_hash = g_steal_pointer (&skip); - - g_once_init_leave (&once, TRUE); - } -} - -SysprofKernelSymbols * -_sysprof_kernel_symbols_new_from_kallsyms (SysprofKallsyms *kallsyms) -{ - static const SysprofKernelSymbol empty = {0}; - SysprofKernelSymbols *self; - const gchar *name; - guint64 addr; - guint8 type; - - do_shared_init (); - - g_return_val_if_fail (kallsyms != NULL, NULL); - - self = g_array_new (FALSE, FALSE, sizeof (SysprofKernelSymbol)); - - G_LOCK (kernel_lock); - - while (sysprof_kallsyms_next (kallsyms, &name, &addr, &type)) - { - if (!type_is_ignored (type)) - { - SysprofKernelSymbol sym; - - sym.address = addr; - sym.name = g_string_chunk_insert_const (kernel_symbol_strs, name); - - g_array_append_val (self, sym); - } - } - - g_array_sort (self, sysprof_kernel_symbol_compare); - - /* Always add a trailing node */ - g_array_append_val (self, empty); - - G_UNLOCK (kernel_lock); - - return g_steal_pointer (&self); -} - -SysprofKernelSymbols * -_sysprof_kernel_symbols_get_shared (void) -{ - static SysprofKernelSymbols *shared; - static SysprofKernelSymbols empty[] = { 0 }; - - if (shared == NULL) - { -#ifdef __linux__ - SysprofHelpers *helpers = sysprof_helpers_get_default (); - g_autofree gchar *contents = NULL; - - if (sysprof_helpers_get_proc_file (helpers, "/proc/kallsyms", NULL, &contents, NULL)) - { - g_autoptr(SysprofKallsyms) kallsyms = sysprof_kallsyms_new_take (g_steal_pointer (&contents)); - shared = _sysprof_kernel_symbols_new_from_kallsyms (kallsyms); - } -#endif - - if (shared == NULL) - shared = empty; - } - - return shared; -} - -static const SysprofKernelSymbol * -sysprof_kernel_symbol_lookup (SysprofKernelSymbol *symbols, - SysprofCaptureAddress address, - guint first, - guint last) -{ - if (symbols == NULL) - return NULL; - - if (address >= symbols [last].address) - { - return &symbols [last]; - } - else if (last - first < 3) - { - while (last >= first) - { - if (address >= symbols[last].address) - return &symbols [last]; - - last--; - } - - return NULL; - } - else - { - int mid = (first + last) / 2; - - if (symbols [mid].address > address) - return sysprof_kernel_symbol_lookup (symbols, address, first, mid); - else - return sysprof_kernel_symbol_lookup (symbols, address, mid, last); - } -} - -/* - * sysprof_kernel_symbols_lookup: - * @self: the symbol data to lookup - * @address: the address of the instruction pointer - * - * Locates the kernel symbol that contains @address. - * - * Returns: (transfer none): An #SysprofKernelSymbol or %NULL. - */ -const SysprofKernelSymbol * -_sysprof_kernel_symbols_lookup (const SysprofKernelSymbols *self, - SysprofCaptureAddress address) -{ - const SysprofKernelSymbol *first; - const SysprofKernelSymbol *ret; - - g_assert (self != NULL); - - if (self->len < 2) - return NULL; - - /* Short circuit if this is out of range */ - first = &g_array_index (self, SysprofKernelSymbol, 0); - if (address < first->address) - return NULL; - - ret = sysprof_kernel_symbol_lookup ((SysprofKernelSymbol *)(gpointer)self->data, - address, - 0, - /* 1 for right-most, 1 for empty node */ - self->len - 2); - - /* We resolve all symbols, including ignored symbols so that we - * don't give back the wrong function juxtapose an ignored func. - */ - if (ret != NULL && g_hash_table_contains (kernel_symbols_skip_hash, ret->name)) - return NULL; - - return ret; -} diff --git a/src/libsysprof/sysprof-kernel-symbol.h b/src/libsysprof/sysprof-kernel-symbol.h deleted file mode 100644 index 27c4790a..00000000 --- a/src/libsysprof/sysprof-kernel-symbol.h +++ /dev/null @@ -1,39 +0,0 @@ -/* sysprof-kernel-symbol.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include - -#include "sysprof-capture-types.h" - -G_BEGIN_DECLS - -typedef struct -{ - SysprofCaptureAddress address; - const gchar *name; -} SysprofKernelSymbol; - -G_END_DECLS diff --git a/src/libsysprof/sysprof-line-reader.c b/src/libsysprof/sysprof-line-reader.c deleted file mode 100644 index 0f10b55e..00000000 --- a/src/libsysprof/sysprof-line-reader.c +++ /dev/null @@ -1,122 +0,0 @@ -/* sysprof-line-reader.c - * - * Copyright 2015-2019 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 - */ - -#include "config.h" - -#include - -#include "sysprof-line-reader.h" - -struct _SysprofLineReader -{ - const gchar *contents; - gsize length; - gsize pos; -}; - -void -sysprof_line_reader_free (SysprofLineReader *self) -{ - g_slice_free (SysprofLineReader, self); -} - -/** - * sysprof_line_reader_new: - * @contents: The buffer to read lines from - * @length: the length of @buffer in bytes - * - * Creates a new #SysprofLineReader for the contents provided. @contents are not - * copied and therefore it is a programming error to free contents before - * freeing the #SysprofLineReader structure. - * - * Use sysprof_line_reader_next() to read through the lines of the buffer. - * - * Returns: (transfer full): A new #SysprofLineReader that should be freed with - * sysprof_line_reader_free() when no longer in use. - */ -SysprofLineReader * -sysprof_line_reader_new (const gchar *contents, - gssize length) -{ - SysprofLineReader *self = g_slice_new (SysprofLineReader); - - if (contents == NULL) - { - contents = ""; - length = 0; - } - else if (length < 0) - { - length = strlen (contents); - } - - self->contents = contents; - self->length = length; - self->pos = 0; - - return self; -} - -/** - * sysprof_line_reader_next: - * @self: the #SysprofLineReader - * @length: a location for the length of the line in bytes - * - * Moves forward to the beginning of the next line in the buffer. No changes to - * the buffer are made, and the result is a pointer within the string passed as - * @contents in sysprof_line_reader_init(). Since the line most likely will not be - * terminated with a NULL byte, you must provide @length to determine the - * length of the line. - * - * Using "line[length]" will place you on the \n that was found for the line. - * However, to perform this safely, you need to know that your string was - * either \0 terminated to begin with, or that your buffer provides enough space - * to guarantee you can dereference past the last "textual content" of the - * buffer. - * - * Returns: (nullable) (transfer none): The beginning of the line within the buffer - */ -const gchar * -sysprof_line_reader_next (SysprofLineReader *self, - gsize *length) -{ - const gchar *ret; - const gchar *endptr; - - g_return_val_if_fail (self != NULL, NULL); - g_return_val_if_fail (length != NULL, NULL); - - if ((self->contents == NULL) || (self->pos >= self->length)) - { - *length = 0; - return NULL; - } - - ret = &self->contents [self->pos]; - - endptr = memchr (ret, '\n', self->length - self->pos); - if (G_UNLIKELY (endptr == NULL)) - endptr = &self->contents [self->length]; - - *length = (endptr - ret); - self->pos += *length + 1; - - return ret; -} diff --git a/src/libsysprof/sysprof-line-reader.h b/src/libsysprof/sysprof-line-reader.h deleted file mode 100644 index 0bb6b303..00000000 --- a/src/libsysprof/sysprof-line-reader.h +++ /dev/null @@ -1,40 +0,0 @@ -/* sysprof-line-reader.h - * - * Copyright 2015-2019 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 - */ - -#pragma once - -#include - -G_BEGIN_DECLS - -typedef struct _SysprofLineReader SysprofLineReader; - -G_GNUC_INTERNAL -SysprofLineReader *sysprof_line_reader_new (const gchar *contents, - gssize length); -G_GNUC_INTERNAL -void sysprof_line_reader_free (SysprofLineReader *self); -G_GNUC_INTERNAL -const gchar *sysprof_line_reader_next (SysprofLineReader *self, - gsize *length); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofLineReader, sysprof_line_reader_free) - -G_END_DECLS diff --git a/src/libsysprof/sysprof-local-profiler.c b/src/libsysprof/sysprof-local-profiler.c deleted file mode 100644 index 59b4aba2..00000000 --- a/src/libsysprof/sysprof-local-profiler.c +++ /dev/null @@ -1,1217 +0,0 @@ -/* sysprof-local-profiler.c - * - * Copyright 2016-2019 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 G_LOG_DOMAIN "sysprof-local-profiler" - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "sysprof-helpers.h" -#include "sysprof-local-profiler.h" -#include "sysprof-platform.h" - -#include "sysprof-capture-autocleanups.h" -#include "sysprof-control-source.h" -#include "sysprof-gjs-source.h" -#include "sysprof-hostinfo-source.h" -#ifdef __linux__ -# include "sysprof-perf-source.h" -#endif -#include "sysprof-proc-source.h" -#include "sysprof-proxy-source.h" - -#define CSTRV(s) ((const gchar * const *)s) - -typedef struct -{ - SysprofCaptureWriter *writer; - - /* All sources added */ - GPtrArray *sources; - - /* Array of GError failures */ - GPtrArray *failures; - - /* Sources currently starting */ - GPtrArray *starting; - - /* Sources currently stopping */ - GPtrArray *stopping; - - /* Sources that have failed or finished */ - GPtrArray *finished_or_failed; - - /* Pids to notify children about before prepare */ - GArray *pids; - - /* Timer for simple time tracking */ - GTimer *timer; - guint timer_notify_source; - - /* Arguments and environment variables for spawning */ - gchar **spawn_argv; - gchar **spawn_env; - gchar *spawn_cwd; - - /* State flags */ - guint is_running : 1; - guint is_stopping : 1; - guint is_starting : 1; - - /* - * If we should spawn argv when starting up. This allows UI to set - * spawn argv/env but enable disable with a toggle. - */ - guint spawn : 1; - - /* If we should inherit the environment when spawning */ - guint spawn_inherit_environ : 1; - - /* If we should inherit stdin from our process */ - guint inherit_stdin : 1; - - /* - * If we should profile the entire system. Setting this results in pids - * being ignored. This is primarily useful for UI to toggle on/off the - * feature of per-process vs whole-system. - */ - guint whole_system : 1; - - /* - * If we got a stop request after calling start() but before we have had - * a chance to settle, then we need to stop immediately after starting. - * We do this to avoid a more complex state machine (for now). - */ - guint stop_after_starting : 1; -} SysprofLocalProfilerPrivate; - -static void profiler_iface_init (SysprofProfilerInterface *iface); - -G_DEFINE_TYPE_EXTENDED (SysprofLocalProfiler, sysprof_local_profiler, G_TYPE_OBJECT, 0, - G_ADD_PRIVATE (SysprofLocalProfiler) - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_PROFILER, profiler_iface_init)) - -enum { - PROP_0, - PROP_INHERIT_STDIN, - N_PROPS, - - PROP_ELAPSED, - PROP_IS_MUTABLE, - PROP_IS_RUNNING, - PROP_SPAWN, - PROP_SPAWN_ARGV, - PROP_SPAWN_CWD, - PROP_SPAWN_ENV, - PROP_SPAWN_INHERIT_ENVIRON, - PROP_WHOLE_SYSTEM, -}; - -enum { - SUBPROCESS_SPAWNED, - SUBPROCESS_FINISHED, - N_SINGALS, -}; - -static GParamSpec *properties [N_PROPS]; -static guint signals [N_SINGALS]; - -static inline gint -_g_ptr_array_find (GPtrArray *ar, - gpointer item) -{ - guint i; - - for (i = 0; i < ar->len; i++) - { - if (item == g_ptr_array_index (ar, i)) - return i; - } - - return -1; -} - -static inline gboolean -_g_ptr_array_contains (GPtrArray *ar, - gpointer item) -{ - return (-1 != _g_ptr_array_find (ar, item)); -} - -static void -sysprof_local_profiler_clear_timer (SysprofLocalProfiler *self) -{ - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - - g_assert (SYSPROF_IS_LOCAL_PROFILER (self)); - - g_clear_pointer (&priv->timer, g_timer_destroy); - - if (priv->timer_notify_source != 0) - { - g_source_remove (priv->timer_notify_source); - priv->timer_notify_source = 0; - } -} - -static void -sysprof_local_profiler_real_stopped (SysprofProfiler *profiler) -{ - SysprofLocalProfiler *self = (SysprofLocalProfiler *)profiler; - - g_assert (SYSPROF_IS_LOCAL_PROFILER (self)); - - sysprof_local_profiler_clear_timer (self); -} - -static gboolean -sysprof_local_profiler_notify_elapsed_cb (gpointer data) -{ - SysprofLocalProfiler *self = data; - - g_assert (SYSPROF_IS_LOCAL_PROFILER (self)); - - g_object_notify (G_OBJECT (self), "elapsed"); - - return G_SOURCE_CONTINUE; -} - -static void -sysprof_local_profiler_finish_stopping (SysprofLocalProfiler *self) -{ - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - g_autoptr(SysprofCaptureReader) reader = NULL; - - g_assert (SYSPROF_IS_LOCAL_PROFILER (self)); - g_assert (priv->is_starting == FALSE); - g_assert (priv->is_stopping == TRUE); - g_assert (priv->stopping->len == 0); - - reader = sysprof_capture_writer_create_reader (priv->writer); - g_assert (reader != NULL); - - for (guint i = 0; i < priv->sources->len; i++) - { - SysprofSource *source = g_ptr_array_index (priv->sources, i); - - sysprof_capture_reader_reset (reader); - sysprof_source_supplement (source, reader); - } - - if (priv->failures->len > 0) - { - const GError *error = g_ptr_array_index (priv->failures, 0); - - sysprof_profiler_emit_failed (SYSPROF_PROFILER (self), error); - } - - priv->is_running = FALSE; - priv->is_stopping = FALSE; - - sysprof_profiler_emit_stopped (SYSPROF_PROFILER (self)); - - g_object_notify (G_OBJECT (self), "is-mutable"); - g_object_notify (G_OBJECT (self), "is-running"); -} - -static void -sysprof_local_profiler_stop (SysprofProfiler *profiler) -{ - SysprofLocalProfiler *self = (SysprofLocalProfiler *)profiler; - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - guint i; - - g_return_if_fail (SYSPROF_IS_LOCAL_PROFILER (self)); - - if (priv->is_starting) - { - priv->stop_after_starting = TRUE; - return; - } - - if (priv->is_stopping || !priv->is_running) - return; - - priv->is_stopping = TRUE; - - /* - * First we add everything to the stopping list, so that we can - * be notified of when they have completed. If everything stopped - * synchronously, the stopping list will be empty after calling - * sysprof_source_stop() for every source. Otherwise, we need to delay - * stopping for a little bit. - */ - - for (i = 0; i < priv->sources->len; i++) - { - SysprofSource *source = g_ptr_array_index (priv->sources, i); - - if (!_g_ptr_array_contains (priv->finished_or_failed, source)) - g_ptr_array_add (priv->stopping, g_object_ref (source)); - } - - for (i = 0; i < priv->sources->len; i++) - { - SysprofSource *source = g_ptr_array_index (priv->sources, i); - - sysprof_source_stop (source); - } - - if (priv->is_stopping && priv->stopping->len == 0) - sysprof_local_profiler_finish_stopping (self); -} - - -static void -sysprof_local_profiler_dispose (GObject *object) -{ - SysprofLocalProfiler *self = (SysprofLocalProfiler *)object; - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - - if (priv->is_running || priv->is_starting) - { - sysprof_local_profiler_stop (SYSPROF_PROFILER (self)); - return; - } - - sysprof_local_profiler_clear_timer (self); - - G_OBJECT_CLASS (sysprof_local_profiler_parent_class)->dispose (object); -} - -static void -sysprof_local_profiler_finalize (GObject *object) -{ - SysprofLocalProfiler *self = (SysprofLocalProfiler *)object; - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - - g_clear_pointer (&priv->writer, sysprof_capture_writer_unref); - g_clear_pointer (&priv->sources, g_ptr_array_unref); - g_clear_pointer (&priv->starting, g_ptr_array_unref); - g_clear_pointer (&priv->stopping, g_ptr_array_unref); - g_clear_pointer (&priv->failures, g_ptr_array_unref); - g_clear_pointer (&priv->finished_or_failed, g_ptr_array_unref); - g_clear_pointer (&priv->pids, g_array_unref); - - G_OBJECT_CLASS (sysprof_local_profiler_parent_class)->finalize (object); -} - -static void -sysprof_local_profiler_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofLocalProfiler *self = SYSPROF_LOCAL_PROFILER (object); - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - - switch (prop_id) - { - case PROP_INHERIT_STDIN: - g_value_set_boolean (value, priv->inherit_stdin); - break; - - case PROP_ELAPSED: - g_value_set_double (value, priv->timer ? g_timer_elapsed (priv->timer, NULL) : 0.0); - break; - - case PROP_IS_MUTABLE: - g_value_set_boolean (value, !(priv->is_starting || priv->is_stopping || priv->is_running)); - break; - - case PROP_IS_RUNNING: - g_value_set_boolean (value, priv->is_running); - break; - - case PROP_WHOLE_SYSTEM: - g_value_set_boolean (value, priv->whole_system); - break; - - case PROP_SPAWN: - g_value_set_boolean (value, priv->spawn); - break; - - case PROP_SPAWN_INHERIT_ENVIRON: - g_value_set_boolean (value, priv->spawn_inherit_environ); - break; - - case PROP_SPAWN_ARGV: - g_value_set_boxed (value, priv->spawn_argv); - break; - - case PROP_SPAWN_CWD: - g_value_set_string (value, priv->spawn_cwd); - break; - - case PROP_SPAWN_ENV: - g_value_set_boxed (value, priv->spawn_env); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_local_profiler_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofLocalProfiler *self = SYSPROF_LOCAL_PROFILER (object); - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - - switch (prop_id) - { - case PROP_INHERIT_STDIN: - sysprof_local_profiler_set_inherit_stdin (self, g_value_get_boolean (value)); - break; - - case PROP_WHOLE_SYSTEM: - priv->whole_system = g_value_get_boolean (value); - break; - - case PROP_SPAWN: - priv->spawn = g_value_get_boolean (value); - break; - - case PROP_SPAWN_INHERIT_ENVIRON: - priv->spawn_inherit_environ = g_value_get_boolean (value); - break; - - case PROP_SPAWN_ARGV: - g_strfreev (priv->spawn_argv); - priv->spawn_argv = g_value_dup_boxed (value); - break; - - case PROP_SPAWN_CWD: - g_free (priv->spawn_cwd); - priv->spawn_cwd = g_value_dup_string (value); - break; - - case PROP_SPAWN_ENV: - g_strfreev (priv->spawn_env); - priv->spawn_env = g_value_dup_boxed (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_local_profiler_class_init (SysprofLocalProfilerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = sysprof_local_profiler_dispose; - object_class->finalize = sysprof_local_profiler_finalize; - object_class->get_property = sysprof_local_profiler_get_property; - object_class->set_property = sysprof_local_profiler_set_property; - - /** - * SysprofLocalProfiler::subprocess-spawned: - * @self: a #SysprofLocalProfiler - * @subprocess: a #GSubprocess - * - * This signal is emitted when #SysprofLocalProfiler spawns a process. - * - * Since: 3.46 - */ - signals [SUBPROCESS_SPAWNED] = - g_signal_new ("subprocess-spawned", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - NULL, - G_TYPE_NONE, 1, G_TYPE_SUBPROCESS); - - /** - * SysprofLocalProfiler::subprocess-finished: - * @self: a #SysprofLocalProfiler - * @subprocess: a #GSubprocess - * - * This signal is emitted when #SysprofLocalProfiler has determined that - * the subprocess either exited or was terminated by a signal. Use - * g_subprocess_get_if_exited() to determine if exited with exit code. - * - * Since: 3.46 - */ - signals [SUBPROCESS_FINISHED] = - g_signal_new ("subprocess-finished", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - NULL, - G_TYPE_NONE, 1, G_TYPE_SUBPROCESS); - - g_object_class_override_property (object_class, PROP_ELAPSED, "elapsed"); - g_object_class_override_property (object_class, PROP_IS_MUTABLE, "is-mutable"); - g_object_class_override_property (object_class, PROP_IS_RUNNING, "is-running"); - g_object_class_override_property (object_class, PROP_SPAWN, "spawn"); - g_object_class_override_property (object_class, PROP_SPAWN_ARGV, "spawn-argv"); - g_object_class_override_property (object_class, PROP_SPAWN_CWD, "spawn-cwd"); - g_object_class_override_property (object_class, PROP_SPAWN_ENV, "spawn-env"); - g_object_class_override_property (object_class, PROP_SPAWN_INHERIT_ENVIRON, "spawn-inherit-environ"); - g_object_class_override_property (object_class, PROP_WHOLE_SYSTEM, "whole-system"); - - /** - * SysprofLocalProfiler:inherit-stdin: - * - * Sets the profiler to inherit stdin from the calling process when spawning - * the subprocess. This has no effect if the #SysprofLocalProfiler is not - * responsible for spawning the process. - * - * Since: 3.46 - */ - properties [PROP_INHERIT_STDIN] = - g_param_spec_boolean ("inherit-stdin", - "Inherit Stdin", - "If stdin of the calling process should be inherited by the spawned process", - FALSE, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - g_type_ensure (SYSPROF_TYPE_GJS_SOURCE); -#ifdef __linux__ - g_type_ensure (SYSPROF_TYPE_HOSTINFO_SOURCE); - g_type_ensure (SYSPROF_TYPE_PROC_SOURCE); - g_type_ensure (SYSPROF_TYPE_PERF_SOURCE); -#endif - g_type_ensure (SYSPROF_TYPE_PROXY_SOURCE); -} - -static void -sysprof_local_profiler_init (SysprofLocalProfiler *self) -{ - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - - priv->whole_system = TRUE; - - priv->failures = g_ptr_array_new_with_free_func ((GDestroyNotify)g_error_free); - priv->sources = g_ptr_array_new_with_free_func (g_object_unref); - priv->starting = g_ptr_array_new_with_free_func (g_object_unref); - priv->stopping = g_ptr_array_new_with_free_func (g_object_unref); - priv->finished_or_failed = g_ptr_array_new_with_free_func (g_object_unref); - priv->pids = g_array_new (FALSE, FALSE, sizeof (GPid)); -} - -SysprofProfiler * -sysprof_local_profiler_new (void) -{ - return g_object_new (SYSPROF_TYPE_LOCAL_PROFILER, NULL); -} - -static void -sysprof_local_profiler_finish_startup (SysprofLocalProfiler *self) -{ - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - guint i; - - g_assert (SYSPROF_IS_LOCAL_PROFILER (self)); - g_assert (priv->is_starting == TRUE); - g_assert (priv->starting->len == 0); - - sysprof_local_profiler_clear_timer (self); - - priv->timer = g_timer_new (); - - /* - * Add a source to update our watchers of elapsed time. - * We use 1000 instead of add_seconds(1) so that we are - * not subject to as much drift. - */ - priv->timer_notify_source = - g_timeout_add (1000, - sysprof_local_profiler_notify_elapsed_cb, - self); - - for (i = 0; i < priv->sources->len; i++) - { - SysprofSource *source = g_ptr_array_index (priv->sources, i); - - sysprof_source_start (source); - } - - priv->is_starting = FALSE; - - /* - * If any of the sources failed during startup, we will have a non-empty - * failures list. - */ - if (priv->failures->len > 0) - { - const GError *error = g_ptr_array_index (priv->failures, 0); - - g_object_ref (self); - sysprof_profiler_emit_failed (SYSPROF_PROFILER (self), error); - sysprof_local_profiler_stop (SYSPROF_PROFILER (self)); - g_object_unref (self); - return; - } - - priv->is_running = TRUE; - - g_object_notify (G_OBJECT (self), "is-mutable"); - g_object_notify (G_OBJECT (self), "is-running"); - - /* - * If all the sources are transient (in that they just generate information - * and then exit), we could be finished as soon as we complete startup. - * - * If we detect this, we stop immediately. - */ - if (priv->finished_or_failed->len == priv->sources->len || priv->stop_after_starting) - sysprof_local_profiler_stop (SYSPROF_PROFILER (self)); -} - -static void -sysprof_local_profiler_wait_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GSubprocess *subprocess = (GSubprocess *)object; - g_autoptr(SysprofLocalProfiler) self = user_data; - g_autoptr(GError) error = NULL; - - g_assert (G_IS_SUBPROCESS (subprocess)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (SYSPROF_IS_LOCAL_PROFILER (self)); - - if (!g_subprocess_wait_finish (subprocess, result, &error)) - g_warning ("Wait on subprocess failed: %s", error->message); - - g_signal_emit (self, signals[SUBPROCESS_FINISHED], 0, subprocess); - - sysprof_local_profiler_stop (SYSPROF_PROFILER (self)); -} - -static void -sysprof_local_profiler_start_after_auth (SysprofLocalProfiler *self) -{ - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - g_autofree gchar *keydata = NULL; - g_autoptr(GError) error = NULL; - g_autoptr(GKeyFile) keyfile = NULL; - gsize keylen = 0; - - g_assert (SYSPROF_IS_LOCAL_PROFILER (self)); - - keyfile = g_key_file_new (); - - g_key_file_set_boolean (keyfile, "profiler", "whole-system", priv->whole_system); - if (priv->pids->len > 0) - g_key_file_set_integer_list (keyfile, "profiler", "pids", - (gint *)(gpointer)priv->pids->data, - priv->pids->len); - g_key_file_set_boolean (keyfile, "profiler", "spawn", priv->spawn); - g_key_file_set_boolean (keyfile, "profiler", "spawn-inherit-environ", priv->spawn_inherit_environ); - g_key_file_set_string (keyfile, "profiler", "spawn-cwd", priv->spawn_cwd ? priv->spawn_cwd : ""); - - if (priv->spawn && priv->spawn_argv && priv->spawn_argv[0]) - { - g_autoptr(GPtrArray) env = g_ptr_array_new_with_free_func (g_free); - g_autoptr(SysprofSpawnable) spawnable = sysprof_spawnable_new (); - g_autoptr(GSubprocess) subprocess = NULL; - GSubprocessFlags flags = 0; - GPid pid; - - if (priv->inherit_stdin) - flags |= G_SUBPROCESS_FLAGS_STDIN_INHERIT; - - if (priv->spawn_inherit_environ) - { - gchar **environ_ = g_get_environ (); - - for (guint i = 0; environ_[i]; i++) - g_ptr_array_add (env, environ_[i]); - g_free (environ_); - } - - if (priv->spawn_env) - { - g_key_file_set_string_list (keyfile, "profiler", "spawn-env", - (const gchar * const *)priv->spawn_env, - g_strv_length (priv->spawn_env)); - for (guint i = 0; priv->spawn_env[i]; i++) - g_ptr_array_add (env, g_strdup (priv->spawn_env[i])); - } - - g_ptr_array_add (env, NULL); - - sysprof_spawnable_set_flags (spawnable, flags); - sysprof_spawnable_set_environ (spawnable, (const gchar * const *)env->pdata); - sysprof_spawnable_append_args (spawnable, (const gchar * const *)priv->spawn_argv); - - if (priv->spawn_cwd != NULL) - sysprof_spawnable_set_cwd (spawnable, priv->spawn_cwd); - - /* Save argv before modifying */ - if (priv->spawn_argv != NULL) - g_key_file_set_string_list (keyfile, - "profiler", - "spawn-argv", - (const gchar * const *)priv->spawn_argv, - g_strv_length (priv->spawn_argv)); - - for (guint i = 0; i < priv->sources->len; i++) - { - SysprofSource *source = g_ptr_array_index (priv->sources, i); - - sysprof_source_modify_spawn (source, spawnable); - } - - if (!(subprocess = sysprof_spawnable_spawn (spawnable, &error))) - { - g_ptr_array_add (priv->failures, g_steal_pointer (&error)); - } - else - { - const gchar *ident = g_subprocess_get_identifier (subprocess); - - pid = atoi (ident); - g_array_append_val (priv->pids, pid); - - g_subprocess_wait_async (subprocess, - NULL, - sysprof_local_profiler_wait_cb, - g_object_ref (self)); - - g_signal_emit (self, signals[SUBPROCESS_SPAWNED], 0, subprocess); - } - } - - g_key_file_set_integer (keyfile, "profiler", "n-sources", priv->sources->len); - - for (guint i = 0; i < priv->sources->len; i++) - { - SysprofSource *source = g_ptr_array_index (priv->sources, i); - g_autofree gchar *group = g_strdup_printf ("source-%u", i); - - g_key_file_set_string (keyfile, group, "gtype", G_OBJECT_TYPE_NAME (source)); - sysprof_source_serialize (source, keyfile, group); - - if (priv->whole_system == FALSE) - { - for (guint j = 0; j < priv->pids->len; j++) - { - GPid pid = g_array_index (priv->pids, GPid, j); - - sysprof_source_add_pid (source, pid); - } - } - - sysprof_source_set_writer (source, priv->writer); - sysprof_source_prepare (source); - } - - for (guint i = 0; i < priv->sources->len; i++) - { - SysprofSource *source = g_ptr_array_index (priv->sources, i); - - if (!sysprof_source_get_is_ready (source)) - g_ptr_array_add (priv->starting, g_object_ref (source)); - } - - if ((keydata = g_key_file_to_data (keyfile, &keylen, NULL))) - sysprof_capture_writer_add_metadata (priv->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - -1, - "local-profiler", - keydata, - keylen); - - if (priv->starting->len == 0) - sysprof_local_profiler_finish_startup (self); -} - -static void -sysprof_local_profiler_preroll_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofHelpers *helpers = (SysprofHelpers *)object; - g_autoptr(SysprofLocalProfiler) self = user_data; - g_autoptr(GError) error = NULL; - - g_assert (SYSPROF_IS_HELPERS (helpers)); - g_assert (SYSPROF_IS_LOCAL_PROFILER (self)); - - /* For almost everything at this point, we need to have authorization - * to the helper daemon. So if this fails, just assume we are going to - * fail in general. It doesn't really help us to optimize for the case - * of user-space only profiling since we are rarely used for that. - */ - - if (!sysprof_helpers_authorize_finish (helpers, result, &error)) - sysprof_profiler_emit_failed (SYSPROF_PROFILER (self), error); - else - sysprof_local_profiler_start_after_auth (self); -} - -static void -sysprof_local_profiler_start (SysprofProfiler *profiler) -{ - SysprofLocalProfiler *self = (SysprofLocalProfiler *)profiler; - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - g_autoptr(SysprofControlSource) control_source = NULL; - - g_return_if_fail (SYSPROF_IS_LOCAL_PROFILER (self)); - g_return_if_fail (priv->is_running == FALSE); - g_return_if_fail (priv->is_stopping == FALSE); - g_return_if_fail (priv->is_starting == FALSE); - - g_clear_pointer (&priv->timer, g_timer_destroy); - g_object_notify (G_OBJECT (self), "elapsed"); - - control_source = sysprof_control_source_new (); - sysprof_profiler_add_source (SYSPROF_PROFILER (self), SYSPROF_SOURCE (control_source)); - - if (priv->writer == NULL) - { - SysprofCaptureWriter *writer; - int fd; - - if ((-1 == (fd = sysprof_memfd_create ("[sysprof]"))) || - (NULL == (writer = sysprof_capture_writer_new_from_fd (fd, 0)))) - { - const GError werror = { - G_FILE_ERROR, - g_file_error_from_errno (errno), - (gchar *)g_strerror (errno) - }; - - if (fd != -1) - close (fd); - - sysprof_profiler_emit_failed (SYSPROF_PROFILER (self), &werror); - - return; - } - - sysprof_profiler_set_writer (SYSPROF_PROFILER (self), writer); - g_clear_pointer (&writer, sysprof_capture_writer_unref); - } - - priv->is_running = TRUE; - priv->is_starting = TRUE; - - if (priv->failures->len > 0) - g_ptr_array_remove_range (priv->failures, 0, priv->failures->len); - - /* Start by prefolling our authorization so that future calls are cheap */ - sysprof_helpers_authorize_async (sysprof_helpers_get_default (), - NULL, - sysprof_local_profiler_preroll_cb, - g_object_ref (self)); -} - -static void -sysprof_local_profiler_set_writer (SysprofProfiler *profiler, - SysprofCaptureWriter *writer) -{ - SysprofLocalProfiler *self = (SysprofLocalProfiler *)profiler; - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_LOCAL_PROFILER (self)); - g_return_if_fail (priv->is_running == FALSE); - g_return_if_fail (priv->is_stopping == FALSE); - g_return_if_fail (writer != NULL); - - if (priv->writer != writer) - { - g_clear_pointer (&priv->writer, sysprof_capture_writer_unref); - - if (writer != NULL) - priv->writer = sysprof_capture_writer_ref (writer); - } -} - -static void -sysprof_local_profiler_track_completed (SysprofLocalProfiler *self, - SysprofSource *source) -{ - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - gint i; - - g_assert (SYSPROF_IS_LOCAL_PROFILER (self)); - g_assert (SYSPROF_IS_SOURCE (source)); - - if (!_g_ptr_array_contains (priv->finished_or_failed, source)) - g_ptr_array_add (priv->finished_or_failed, g_object_ref (source)); - - if (priv->is_starting) - { - i = _g_ptr_array_find (priv->starting, source); - - if (i >= 0) - { - g_ptr_array_remove_index (priv->starting, i); - if (priv->starting->len == 0) - sysprof_local_profiler_finish_startup (self); - } - } - - if (priv->is_stopping) - { - i = _g_ptr_array_find (priv->stopping, source); - - if (i >= 0) - { - g_ptr_array_remove_index_fast (priv->stopping, i); - - if ((priv->is_stopping == TRUE) && (priv->stopping->len == 0)) - sysprof_local_profiler_finish_stopping (self); - } - } - - if (!priv->is_starting) - { - if (priv->finished_or_failed->len == priv->sources->len) - sysprof_local_profiler_stop (SYSPROF_PROFILER (self)); - } -} - -static void -sysprof_local_profiler_source_finished (SysprofLocalProfiler *self, - SysprofSource *source) -{ - g_assert (SYSPROF_IS_LOCAL_PROFILER (self)); - g_assert (SYSPROF_IS_SOURCE (source)); - - sysprof_local_profiler_track_completed (self, source); -} - -static void -sysprof_local_profiler_source_ready (SysprofLocalProfiler *self, - SysprofSource *source) -{ - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - guint i; - - g_assert (SYSPROF_IS_LOCAL_PROFILER (self)); - g_assert (SYSPROF_IS_SOURCE (source)); - - for (i = 0; i < priv->starting->len; i++) - { - SysprofSource *ele = g_ptr_array_index (priv->starting, i); - - if (ele == source) - { - g_ptr_array_remove_index_fast (priv->starting, i); - - if ((priv->is_starting == TRUE) && (priv->starting->len == 0)) - sysprof_local_profiler_finish_startup (self); - - break; - } - } -} - -static void -sysprof_local_profiler_source_failed (SysprofLocalProfiler *self, - const GError *reason, - SysprofSource *source) -{ - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - - g_assert (SYSPROF_IS_LOCAL_PROFILER (self)); - g_assert (reason != NULL); - g_assert (SYSPROF_IS_SOURCE (source)); - - g_warning ("%s failed: %s", - G_OBJECT_TYPE_NAME (source), - reason ? reason->message : "unknown error"); - - sysprof_local_profiler_track_completed (self, source); - - /* Failure emitted out of band */ - if (!priv->is_starting && !priv->is_stopping && !priv->is_running) - return; - - g_ptr_array_add (priv->failures, g_error_copy (reason)); - - /* Ignore during start/stop, we handle this in other places */ - if (priv->is_starting || priv->is_stopping) - return; - - if (priv->is_running) - sysprof_local_profiler_stop (SYSPROF_PROFILER (self)); -} - -static void -sysprof_local_profiler_add_source (SysprofProfiler *profiler, - SysprofSource *source) -{ - SysprofLocalProfiler *self = (SysprofLocalProfiler *)profiler; - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_LOCAL_PROFILER (self)); - g_return_if_fail (SYSPROF_IS_SOURCE (source)); - g_return_if_fail (priv->is_running == FALSE); - g_return_if_fail (priv->is_starting == FALSE); - g_return_if_fail (priv->is_stopping == FALSE); - - g_signal_connect_object (source, - "failed", - G_CALLBACK (sysprof_local_profiler_source_failed), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (source, - "finished", - G_CALLBACK (sysprof_local_profiler_source_finished), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (source, - "ready", - G_CALLBACK (sysprof_local_profiler_source_ready), - self, - G_CONNECT_SWAPPED); - - - g_ptr_array_add (priv->sources, g_object_ref (source)); -} - -static void -sysprof_local_profiler_add_pid (SysprofProfiler *profiler, - GPid pid) -{ - SysprofLocalProfiler *self = (SysprofLocalProfiler *)profiler; - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_LOCAL_PROFILER (self)); - g_return_if_fail (pid > -1); - g_return_if_fail (priv->is_starting == FALSE); - g_return_if_fail (priv->is_stopping == FALSE); - g_return_if_fail (priv->is_running == FALSE); - - g_array_append_val (priv->pids, pid); -} - -static void -sysprof_local_profiler_remove_pid (SysprofProfiler *profiler, - GPid pid) -{ - SysprofLocalProfiler *self = (SysprofLocalProfiler *)profiler; - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - guint i; - - g_return_if_fail (SYSPROF_IS_LOCAL_PROFILER (self)); - g_return_if_fail (pid > -1); - g_return_if_fail (priv->is_starting == FALSE); - g_return_if_fail (priv->is_stopping == FALSE); - g_return_if_fail (priv->is_running == FALSE); - - for (i = 0; i < priv->pids->len; i++) - { - GPid ele = g_array_index (priv->pids, GPid, i); - - if (ele == pid) - { - g_array_remove_index_fast (priv->pids, i); - break; - } - } -} - -static const GPid * -sysprof_local_profiler_get_pids (SysprofProfiler *profiler, - guint *n_pids) -{ - SysprofLocalProfiler *self = (SysprofLocalProfiler *)profiler; - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_LOCAL_PROFILER (self), NULL); - g_return_val_if_fail (n_pids != NULL, NULL); - - *n_pids = priv->pids->len; - - return (GPid *)(gpointer)priv->pids->data; -} - -static SysprofCaptureWriter * -sysprof_local_profiler_get_writer (SysprofProfiler *profiler) -{ - SysprofLocalProfiler *self = (SysprofLocalProfiler *)profiler; - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_LOCAL_PROFILER (self), NULL); - - return priv->writer; -} - -static void -profiler_iface_init (SysprofProfilerInterface *iface) -{ - iface->add_pid = sysprof_local_profiler_add_pid; - iface->add_source = sysprof_local_profiler_add_source; - iface->get_pids = sysprof_local_profiler_get_pids; - iface->get_writer = sysprof_local_profiler_get_writer; - iface->remove_pid = sysprof_local_profiler_remove_pid; - iface->set_writer = sysprof_local_profiler_set_writer; - iface->start = sysprof_local_profiler_start; - iface->stop = sysprof_local_profiler_stop; - iface->stopped = sysprof_local_profiler_real_stopped; -} - -static bool -find_profiler_meta_cb (const SysprofCaptureFrame *frame, - void *user_data) -{ - const SysprofCaptureMetadata *meta = (const SysprofCaptureMetadata *)frame; - GKeyFile **keyfile = user_data; - - g_assert (frame != NULL); - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_METADATA); - g_assert (keyfile != NULL); - g_assert (*keyfile == NULL); - - if (g_strcmp0 (meta->id, "local-profiler") == 0) - { - g_autoptr(GKeyFile) kf = g_key_file_new (); - - /* Metadata is guaranteed to be \0 terminated by marshaller */ - if (g_key_file_load_from_data (kf, meta->metadata, -1, 0, NULL)) - *keyfile = g_steal_pointer (&kf); - - return *keyfile == NULL; - } - - return true; -} - -SysprofProfiler * -sysprof_local_profiler_new_replay (SysprofCaptureReader *reader) -{ - static const SysprofCaptureFrameType mtype[] = { - SYSPROF_CAPTURE_FRAME_METADATA, - }; - g_autoptr(SysprofLocalProfiler) self = NULL; - g_autoptr(SysprofCaptureCursor) cursor = NULL; - g_autoptr(GKeyFile) keyfile = NULL; - g_autofree gchar *cwd = NULL; - g_auto(GStrv) argv = NULL; - g_auto(GStrv) env = NULL; - gboolean whole_system; - gboolean inherit; - gboolean spawn; - gint n_sources; - - g_return_val_if_fail (reader != NULL, NULL); - - self = g_object_new (SYSPROF_TYPE_LOCAL_PROFILER, NULL); - - cursor = sysprof_capture_cursor_new (reader); - sysprof_capture_cursor_add_condition (cursor, sysprof_capture_condition_new_where_type_in (1, mtype)); - sysprof_capture_cursor_foreach (cursor, find_profiler_meta_cb, &keyfile); - - /* No metadata, bail */ - if (keyfile == NULL) - return NULL; - - spawn = g_key_file_get_boolean (keyfile, "profiler", "spawn", NULL); - inherit = g_key_file_get_boolean (keyfile, "profiler", "spawn-inherit-environ", NULL); - argv = g_key_file_get_string_list (keyfile, "profiler", "spawn-argv", NULL, NULL); - env = g_key_file_get_string_list (keyfile, "profiler", "spawn-env", NULL, NULL); - cwd = g_key_file_get_string (keyfile, "profiler", "spawn-cwd", NULL); - n_sources = g_key_file_get_integer (keyfile, "profiler", "n-sources", NULL); - whole_system = g_key_file_get_boolean (keyfile, "profiler", "whole-system", NULL); - - /* Ignore empty string */ - if (cwd != NULL && *cwd == 0) - g_clear_pointer (&cwd, g_free); - - sysprof_profiler_set_spawn (SYSPROF_PROFILER (self), spawn); - sysprof_profiler_set_spawn_argv (SYSPROF_PROFILER (self), CSTRV (argv)); - sysprof_profiler_set_spawn_cwd (SYSPROF_PROFILER (self), cwd); - sysprof_profiler_set_spawn_env (SYSPROF_PROFILER (self), CSTRV (env)); - sysprof_profiler_set_spawn_inherit_environ (SYSPROF_PROFILER (self), inherit); - sysprof_profiler_set_whole_system (SYSPROF_PROFILER (self), whole_system); - - for (guint i = 0; i < n_sources; i++) - { - g_autofree gchar *group = g_strdup_printf ("source-%u", i); - g_autofree gchar *type_name = NULL; - g_autoptr(SysprofSource) source = NULL; - GType gtype; - - if (!g_key_file_has_group (keyfile, group) || - !(type_name = g_key_file_get_string (keyfile, group, "gtype", NULL)) || - !(gtype = g_type_from_name (type_name)) || - !g_type_is_a (gtype, SYSPROF_TYPE_SOURCE) || - !(source = g_object_new (gtype, NULL))) - continue; - - sysprof_source_deserialize (source, keyfile, group); - sysprof_local_profiler_add_source (SYSPROF_PROFILER (self), source); - } - - return SYSPROF_PROFILER (g_steal_pointer (&self)); -} - -/** - * sysprof_local_profiler_get_inherit_stdin: - * - * Since: 3.46 - */ -gboolean -sysprof_local_profiler_get_inherit_stdin (SysprofLocalProfiler *self) -{ - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_LOCAL_PROFILER (self), FALSE); - - return priv->inherit_stdin; -} - -/** - * sysprof_local_profiler_set_inherit_stdin: - * - * Since: 3.46 - */ -void -sysprof_local_profiler_set_inherit_stdin (SysprofLocalProfiler *self, - gboolean inherit_stdin) -{ - SysprofLocalProfilerPrivate *priv = sysprof_local_profiler_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_LOCAL_PROFILER (self)); - - inherit_stdin = !!inherit_stdin; - - if (priv->inherit_stdin != inherit_stdin) - { - priv->inherit_stdin = inherit_stdin; - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_INHERIT_STDIN]); - } -} diff --git a/src/libsysprof/sysprof-local-profiler.h b/src/libsysprof/sysprof-local-profiler.h deleted file mode 100644 index 469f93c8..00000000 --- a/src/libsysprof/sysprof-local-profiler.h +++ /dev/null @@ -1,51 +0,0 @@ -/* sysprof-local-profiler.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include "sysprof-profiler.h" -#include "sysprof-version-macros.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_LOCAL_PROFILER (sysprof_local_profiler_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_DERIVABLE_TYPE (SysprofLocalProfiler, sysprof_local_profiler, SYSPROF, LOCAL_PROFILER, GObject) - -struct _SysprofLocalProfilerClass -{ - GObjectClass parent_class; - gpointer padding[8]; -}; - -SYSPROF_AVAILABLE_IN_ALL -SysprofProfiler *sysprof_local_profiler_new (void); -SYSPROF_AVAILABLE_IN_ALL -SysprofProfiler *sysprof_local_profiler_new_replay (SysprofCaptureReader *reader); -SYSPROF_AVAILABLE_IN_3_46 -void sysprof_local_profiler_set_inherit_stdin (SysprofLocalProfiler *self, - gboolean inherit_stdin); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-map-lookaside.c b/src/libsysprof/sysprof-map-lookaside.c deleted file mode 100644 index 719b93c2..00000000 --- a/src/libsysprof/sysprof-map-lookaside.c +++ /dev/null @@ -1,143 +0,0 @@ -/* sysprof-map-lookaside.c - * - * Copyright 2016-2019 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 - */ - -#include "config.h" - -#include - -#include "sysprof-map-lookaside.h" - -static gint -sysprof_map_compare (gconstpointer a, - gconstpointer b, - gpointer user_data) -{ - const SysprofMap *map_a = a; - const SysprofMap *map_b = b; - - return sysprof_capture_address_compare (map_a->start, map_b->start); -} - -static gint -sysprof_map_compare_in_range (gconstpointer a, - gconstpointer b, - gpointer user_data) -{ - const SysprofMap *map_a = a; - const SysprofMap *map_b = b; - - /* - * map_b is the needle for the search. - * Only map_b->start is set. - */ - - if ((map_b->start >= map_a->start) && (map_b->start < map_a->end)) - return 0; - - return sysprof_capture_address_compare (map_a->start, map_b->start); -} - -static void -sysprof_map_free (gpointer data) -{ - SysprofMap *map = data; - - g_slice_free (SysprofMap, map); -} - -SysprofMapLookaside * -sysprof_map_lookaside_new (void) -{ - SysprofMapLookaside *ret; - - ret = g_slice_new0 (SysprofMapLookaside); - ret->seq = g_sequence_new (sysprof_map_free); - ret->chunk = g_string_chunk_new (4096); - ret->overlays = NULL; - - return ret; -} - -void -sysprof_map_lookaside_free (SysprofMapLookaside *self) -{ - g_sequence_free (self->seq); - g_string_chunk_free (self->chunk); - g_slice_free (SysprofMapLookaside, self); -} - -void -sysprof_map_lookaside_insert (SysprofMapLookaside *self, - const SysprofMap *map) -{ - SysprofMap *copy; - - g_assert (self != NULL); - g_assert (map != NULL); - - copy = g_slice_new (SysprofMap); - copy->start = map->start; - copy->end = map->end; - copy->offset = map->offset; - copy->inode = map->inode; - copy->filename = g_string_chunk_insert_const (self->chunk, map->filename); - - g_sequence_insert_sorted (self->seq, copy, sysprof_map_compare, NULL); -} - - -void -sysprof_map_lookaside_overlay (SysprofMapLookaside *self, - const gchar *src, - const gchar *dst) -{ - SysprofMapOverlay overlay; - - g_assert (self != NULL); - g_assert (src != NULL); - g_assert (dst != NULL); - - if (!src[0] || !dst[0]) - return; - - if (self->overlays == NULL) - self->overlays = g_array_new (FALSE, FALSE, sizeof (SysprofMapOverlay)); - - overlay.src = g_string_chunk_insert_const (self->chunk, src); - overlay.dst = g_string_chunk_insert_const (self->chunk, dst); - g_array_append_val (self->overlays, overlay); -} - -const SysprofMap * -sysprof_map_lookaside_lookup (SysprofMapLookaside *self, - SysprofCaptureAddress address) -{ - SysprofMap map = { address }; - GSequenceIter *iter; - - g_assert (self != NULL); - - iter = g_sequence_lookup (self->seq, &map, sysprof_map_compare_in_range, NULL); - - if (iter != NULL) - return g_sequence_get (iter); - - return NULL; -} diff --git a/src/libsysprof/sysprof-map-lookaside.h b/src/libsysprof/sysprof-map-lookaside.h deleted file mode 100644 index 9cbc8b9d..00000000 --- a/src/libsysprof/sysprof-map-lookaside.h +++ /dev/null @@ -1,63 +0,0 @@ -/* sysprof-map-lookaside.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#include - -#include "sysprof-capture-types.h" - -G_BEGIN_DECLS - -typedef struct _SysprofMapOverlay -{ - const char *src; - const char *dst; -} SysprofMapOverlay; - -typedef struct _SysprofMapLookaside -{ - GSequence *seq; - GStringChunk *chunk; - GArray *overlays; -} SysprofMapLookaside; - -typedef struct -{ - SysprofCaptureAddress start; - SysprofCaptureAddress end; - off_t offset; - ino_t inode; - const gchar *filename; -} SysprofMap; - -SysprofMapLookaside *sysprof_map_lookaside_new (void); -void sysprof_map_lookaside_insert (SysprofMapLookaside *self, - const SysprofMap *map); -void sysprof_map_lookaside_overlay (SysprofMapLookaside *self, - const gchar *src, - const gchar *dst); -const SysprofMap *sysprof_map_lookaside_lookup (SysprofMapLookaside *self, - SysprofCaptureAddress address); -void sysprof_map_lookaside_free (SysprofMapLookaside *self); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofMapLookaside, sysprof_map_lookaside_free) - -G_END_DECLS diff --git a/src/libsysprof/sysprof-memory-source.c b/src/libsysprof/sysprof-memory-source.c deleted file mode 100644 index 20cc88c9..00000000 --- a/src/libsysprof/sysprof-memory-source.c +++ /dev/null @@ -1,473 +0,0 @@ -/* sysprof-memory-source.c - * - * Copyright 2018-2019 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 G_LOG_DOMAIN "sysprof-memory-source" - -#include "config.h" - -#include -#include -#include -#include -#include -#include - -#include "sysprof-helpers.h" -#include "sysprof-memory-source.h" - -#define BUF_SIZE 4096 - -struct _SysprofMemorySource -{ - GObject parent_instance; - - /* Capture writer to deliver samples */ - SysprofCaptureWriter *writer; - - /* 4k stat buffer for reading proc */ - gchar *stat_buf; - - /* Array of MemStat */ - GArray *mem_stats; - - /* Timeout to perform polling */ - guint timer_source; -}; - -typedef struct -{ - GPid pid; - int stat_fd; - - union { - struct { - SysprofCaptureCounterValue used; - gint64 total; - gint64 avail; - gint64 free; - } sys; - struct { - SysprofCaptureCounterValue used; - gint64 size; - gint64 resident; - gint64 shared; - gint64 text; - gint64 data; - } proc; - }; - - guint counter_ids[1]; -} MemStat; - -static void source_iface_init (SysprofSourceInterface *iface); - -G_DEFINE_TYPE_WITH_CODE (SysprofMemorySource, sysprof_memory_source, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init)) - -static GHashTable *keys; - -static void -mem_stat_open (MemStat *st) -{ - SysprofHelpers *helpers = sysprof_helpers_get_default (); - g_autoptr(GError) error = NULL; - - g_assert (st != NULL); - g_assert (st->stat_fd == -1); - - if (st->pid != -1) - { - g_autofree gchar *path = g_strdup_printf ("/proc/%d/statm", st->pid); - - if (!sysprof_helpers_get_proc_fd (helpers, path, NULL, &st->stat_fd, &error)) - g_warning ("Failed to access statm for pid %d: %s", st->pid, error->message); - } - else - { - if (!sysprof_helpers_get_proc_fd (helpers, "/proc/meminfo", NULL, &st->stat_fd, &error)) - g_warning ("Failed to access /proc/statm: %s", error->message); - } -} - -static void -mem_stat_close (MemStat *st) -{ - g_assert (st != NULL); - - if (st->stat_fd != -1) - { - close (st->stat_fd); - st->stat_fd = -1; - } -} - -static void -mem_stat_parse_statm (MemStat *st, - gchar *buf) -{ - g_assert (st != NULL); - g_assert (buf != NULL); - - sscanf (buf, - "%"G_GINT64_FORMAT" " - "%"G_GINT64_FORMAT" " - "%"G_GINT64_FORMAT" " - "%"G_GINT64_FORMAT" " - "%*1c " - "%"G_GINT64_FORMAT, - &st->proc.size, - &st->proc.resident, - &st->proc.shared, - &st->proc.text, - &st->proc.data); - - st->proc.used.vdbl = st->proc.size - st->proc.shared - st->proc.text - st->proc.data; -} - -static void -mem_stat_parse_meminfo (MemStat *st, - gchar *buf) -{ - gchar *bufptr = buf; - gchar *save = NULL; - - g_assert (st != NULL); - g_assert (buf != NULL); - - for (;;) - { - goffset off; - gchar *key; - gchar *value; - gchar *unit; - gint64 v64; - gint64 *v64ptr; - - /* Get the data key name */ - if (!(key = strtok_r (bufptr, " \n\t:", &save))) - break; - - bufptr = NULL; - - /* Offset from self to save value. Stop after getting to - * last value we care about. - */ - if (!(off = GPOINTER_TO_UINT (g_hash_table_lookup (keys, key)))) - break; - - /* Get the data value */ - if (!(value = strtok_r (bufptr, " \n\t:", &save))) - break; - - /* Parse the numeric value of this column */ - v64 = g_ascii_strtoll (value, NULL, 10); - if ((v64 == G_MININT64 || v64 == G_MAXINT64) && errno == ERANGE) - break; - - /* Get the data unit */ - unit = strtok_r (bufptr, " \n\t:", &save); - - if (g_strcmp0 (unit, "kB") == 0) - v64 *= 1024; - else if (g_strcmp0 (unit, "mB") == 0) - v64 *= 1024 * 1024; - - v64ptr = (gint64 *)(gpointer)(((gchar *)st) + off); - - *v64ptr = v64; - } - - /* Create pre-compiled value for used to simplify display */ - st->sys.used.vdbl = (gdouble)st->sys.total - (gdouble)st->sys.avail; -} - -static void -mem_stat_poll (MemStat *st, - gchar *stat_buf) -{ - gssize r; - - g_assert (st != NULL); - g_assert (st->stat_fd != -1); - - /* Seek to begin of FD to reset the proc read */ - if ((r = lseek (st->stat_fd, 0, SEEK_SET)) < 0) - return; - - /* Now read the updated proc buffer */ - if ((r = read (st->stat_fd, stat_buf, BUF_SIZE)) < 0) - return; - - if (r < BUF_SIZE) - stat_buf[r] = '\0'; - stat_buf[BUF_SIZE-1] = '\0'; - - if (st->pid == -1) - mem_stat_parse_meminfo (st, stat_buf); - else - mem_stat_parse_statm (st, stat_buf); -} - -static void -mem_stat_publish (MemStat *st, - SysprofCaptureWriter *writer, - gint64 current_time) -{ - g_assert (st != NULL); - g_assert (writer != NULL); - - sysprof_capture_writer_set_counters (writer, - current_time, - -1, - st->pid, - st->counter_ids, - st->pid == -1 ? &st->sys.used : &st->proc.used, - 1); -} - -/** - * sysprof_memory_source_new: - * - * Create a new #SysprofMemorySource. - * - * Use this source to record basic memory statistics about the system - * such as Available, Free, and Total Memory. - * - * Returns: (transfer full): a newly created #SysprofMemorySource - - * Since: 3.32 - */ -SysprofSource * -sysprof_memory_source_new (void) -{ - return g_object_new (SYSPROF_TYPE_MEMORY_SOURCE, NULL); -} - -static void -sysprof_memory_source_finalize (GObject *object) -{ - SysprofMemorySource *self = (SysprofMemorySource *)object; - - if (self->timer_source != 0) - { - g_source_remove (self->timer_source); - self->timer_source = 0; - } - - g_clear_pointer (&self->stat_buf, g_free); - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); - g_clear_pointer (&self->mem_stats, g_array_unref); - - G_OBJECT_CLASS (sysprof_memory_source_parent_class)->finalize (object); -} - -static void -sysprof_memory_source_class_init (SysprofMemorySourceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_memory_source_finalize; - - keys = g_hash_table_new (g_str_hash, g_str_equal); - -#define ADD_OFFSET(n,o) \ - g_hash_table_insert (keys, (gchar *)n, GUINT_TO_POINTER (o)) - ADD_OFFSET ("MemTotal", G_STRUCT_OFFSET (MemStat, sys.total)); - ADD_OFFSET ("MemFree", G_STRUCT_OFFSET (MemStat, sys.free)); - ADD_OFFSET ("MemAvailable", G_STRUCT_OFFSET (MemStat, sys.avail)); -#undef ADD_OFFSET -} - -static void -sysprof_memory_source_init (SysprofMemorySource *self) -{ - self->stat_buf = g_malloc (BUF_SIZE); - self->mem_stats = g_array_new (FALSE, FALSE, sizeof (MemStat)); -} - -static void -sysprof_memory_source_set_writer (SysprofSource *source, - SysprofCaptureWriter *writer) -{ - SysprofMemorySource *self = (SysprofMemorySource *)source; - - g_assert (SYSPROF_IS_SOURCE (self)); - g_assert (writer != NULL); - g_assert (self->writer == NULL); - - self->writer = sysprof_capture_writer_ref (writer); -} - -static void -sysprof_memory_source_prepare (SysprofSource *source) -{ - SysprofMemorySource *self = (SysprofMemorySource *)source; - - g_assert (SYSPROF_IS_MEMORY_SOURCE (self)); - g_assert (self->writer != NULL); - - /* If no pids are registered, setup a system memory stat */ - if (self->mem_stats->len == 0) - { - MemStat st = {0}; - - st.pid = -1; - st.stat_fd = -1; - - g_array_append_val (self->mem_stats, st); - } - - /* Open FDs to all the necessary files in proc. We will re-use - * them to avoid re-opening files later. - */ - for (guint i = 0; i < self->mem_stats->len; i++) - { - MemStat *st = &g_array_index (self->mem_stats, MemStat, i); - SysprofCaptureCounter counters[5]; - guint base; - - mem_stat_open (st); - - if (st->pid == -1) - { - base = sysprof_capture_writer_request_counter (self->writer, 1); - - g_strlcpy (counters[0].category, "Memory", sizeof counters[0].category); - g_strlcpy (counters[0].name, "Used", sizeof counters[0].name); - g_strlcpy (counters[0].description, "Memory used by system", sizeof counters[0].description); - - counters[0].id = st->counter_ids[0] = base; - counters[0].type = SYSPROF_CAPTURE_COUNTER_DOUBLE; - counters[0].value.vdbl = 0; - - sysprof_capture_writer_define_counters (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - -1, - counters, - 1); - } - else - { - base = sysprof_capture_writer_request_counter (self->writer, 1); - - g_strlcpy (counters[0].category, "Memory", sizeof counters[0].category); - g_strlcpy (counters[0].name, "Used", sizeof counters[0].name); - g_strlcpy (counters[0].description, "Memory used by process", sizeof counters[0].description); - - counters[0].id = st->counter_ids[0] = base; - counters[0].type = SYSPROF_CAPTURE_COUNTER_DOUBLE; - counters[0].value.vdbl = 0; - - sysprof_capture_writer_define_counters (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - st->pid, - counters, - 1); - } - } - - sysprof_source_emit_ready (SYSPROF_SOURCE (self)); -} - -static gboolean -sysprof_memory_source_timer_cb (SysprofMemorySource *self) -{ - gint64 current_time; - - g_assert (SYSPROF_IS_MEMORY_SOURCE (self)); - g_assert (self->writer != NULL); - - current_time = sysprof_clock_get_current_time (); - - for (guint i = 0; i < self->mem_stats->len; i++) - { - MemStat *st = &g_array_index (self->mem_stats, MemStat, i); - - mem_stat_poll (st, self->stat_buf); - mem_stat_publish (st, self->writer, current_time); - } - - return G_SOURCE_CONTINUE; -} - -static void -sysprof_memory_source_start (SysprofSource *source) -{ - SysprofMemorySource *self = (SysprofMemorySource *)source; - - g_assert (SYSPROF_IS_MEMORY_SOURCE (self)); - - /* Poll 4x/sec for memory stats */ - self->timer_source = g_timeout_add_full (G_PRIORITY_HIGH, - 1000 / 4, - (GSourceFunc)sysprof_memory_source_timer_cb, - self, - NULL); -} - -static void -sysprof_memory_source_stop (SysprofSource *source) -{ - SysprofMemorySource *self = (SysprofMemorySource *)source; - - g_assert (SYSPROF_IS_MEMORY_SOURCE (self)); - - if (self->timer_source != 0) - { - g_source_remove (self->timer_source); - self->timer_source = 0; - } - - for (guint i = 0; i < self->mem_stats->len; i++) - { - MemStat *st = &g_array_index (self->mem_stats, MemStat, i); - - mem_stat_close (st); - } - - sysprof_source_emit_finished (source); -} - -static void -sysprof_memory_source_add_pid (SysprofSource *source, - GPid pid) -{ - SysprofMemorySource *self = (SysprofMemorySource *)source; - MemStat st = {0}; - - g_assert (SYSPROF_IS_MEMORY_SOURCE (self)); - - st.pid = pid; - st.stat_fd = -1; - - g_array_append_val (self->mem_stats, st); -} - -static void -source_iface_init (SysprofSourceInterface *iface) -{ - iface->set_writer = sysprof_memory_source_set_writer; - iface->prepare = sysprof_memory_source_prepare; - iface->start = sysprof_memory_source_start; - iface->stop = sysprof_memory_source_stop; - iface->add_pid = sysprof_memory_source_add_pid; -} diff --git a/src/libsysprof/sysprof-memory-source.h b/src/libsysprof/sysprof-memory-source.h deleted file mode 100644 index 8e70e45a..00000000 --- a/src/libsysprof/sysprof-memory-source.h +++ /dev/null @@ -1,40 +0,0 @@ -/* sysprof-memory-source.h - * - * Copyright 2018-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include "sysprof-source.h" -#include "sysprof-version-macros.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_MEMORY_SOURCE (sysprof_memory_source_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofMemorySource, sysprof_memory_source, SYSPROF, MEMORY_SOURCE, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSource *sysprof_memory_source_new (void); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-memprof-profile.c b/src/libsysprof/sysprof-memprof-profile.c deleted file mode 100644 index 33da6bc5..00000000 --- a/src/libsysprof/sysprof-memprof-profile.c +++ /dev/null @@ -1,1173 +0,0 @@ -/* sysprof-memprof-profile.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 G_LOG_DOMAIN "sysprof-memprof-profile" - -#include "config.h" - -#include - -#include "sysprof-capture-autocleanups.h" -#include "sysprof-capture-symbol-resolver.h" -#include "sysprof-elf-symbol-resolver.h" -#include "sysprof-kernel-symbol-resolver.h" -#include "sysprof-memprof-profile.h" -#include "sysprof-symbol-resolver.h" - -#include "rax.h" -#include "../stackstash.h" - -typedef struct -{ - gint pid; - gint tid; - gint64 time; - SysprofCaptureAddress addr; - gint64 size; - guint64 frame_num; -} Alloc; - -typedef struct -{ - volatile gint ref_count; - SysprofSelection *selection; - SysprofCaptureReader *reader; - GPtrArray *resolvers; - GStringChunk *symbols; - GHashTable *tags; - GHashTable *cmdlines; - StackStash *stash; - StackStash *building; - rax *rax; - GArray *resolved; - SysprofMemprofMode mode; - SysprofMemprofStats stats; -} Generate; - -struct _SysprofMemprofProfile -{ - GObject parent_instance; - SysprofSelection *selection; - SysprofCaptureReader *reader; - Generate *g; - SysprofMemprofMode mode; -}; - -static void profile_iface_init (SysprofProfileInterface *iface); - -G_DEFINE_TYPE_WITH_CODE (SysprofMemprofProfile, sysprof_memprof_profile, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_PROFILE, profile_iface_init)) - -enum { - PROP_0, - PROP_SELECTION, - N_PROPS -}; - -static GParamSpec *properties[N_PROPS]; - -static void -generate_finalize (Generate *g) -{ - g_clear_pointer (&g->reader, sysprof_capture_reader_unref); - g_clear_pointer (&g->rax, raxFree); - g_clear_pointer (&g->stash, stack_stash_unref); - g_clear_pointer (&g->building, stack_stash_unref); - g_clear_pointer (&g->resolvers, g_ptr_array_unref); - g_clear_pointer (&g->symbols, g_string_chunk_free); - g_clear_pointer (&g->tags, g_hash_table_unref); - g_clear_pointer (&g->resolved, g_array_unref); - g_clear_pointer (&g->cmdlines, g_hash_table_unref); - g_clear_object (&g->selection); - g_slice_free (Generate, g); -} - -static Generate * -generate_ref (Generate *g) -{ - g_return_val_if_fail (g != NULL, NULL); - g_return_val_if_fail (g->ref_count > 0, NULL); - - g_atomic_int_inc (&g->ref_count); - - return g; -} - -static void -generate_unref (Generate *g) -{ - g_return_if_fail (g != NULL); - g_return_if_fail (g->ref_count > 0); - - if (g_atomic_int_dec_and_test (&g->ref_count)) - generate_finalize (g); -} - -static void -sysprof_memprof_profile_finalize (GObject *object) -{ - SysprofMemprofProfile *self = (SysprofMemprofProfile *)object; - - g_clear_pointer (&self->g, generate_unref); - g_clear_pointer (&self->reader, sysprof_capture_reader_unref); - g_clear_object (&self->selection); - - G_OBJECT_CLASS (sysprof_memprof_profile_parent_class)->finalize (object); -} - -static void -sysprof_memprof_profile_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofMemprofProfile *self = SYSPROF_MEMPROF_PROFILE (object); - - switch (prop_id) - { - case PROP_SELECTION: - g_value_set_object (value, self->selection); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_memprof_profile_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofMemprofProfile *self = SYSPROF_MEMPROF_PROFILE (object); - - switch (prop_id) - { - case PROP_SELECTION: - self->selection = g_value_dup_object (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_memprof_profile_class_init (SysprofMemprofProfileClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_memprof_profile_finalize; - object_class->get_property = sysprof_memprof_profile_get_property; - object_class->set_property = sysprof_memprof_profile_set_property; - - properties [PROP_SELECTION] = - g_param_spec_object ("selection", - "Selection", - "The selection for filtering the callgraph", - SYSPROF_TYPE_SELECTION, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_memprof_profile_init (SysprofMemprofProfile *self) -{ - self->mode = SYSPROF_MEMPROF_MODE_ALL_ALLOCS; -} - -SysprofMemprofMode -sysprof_memprof_profile_get_mode (SysprofMemprofProfile *self) -{ - g_return_val_if_fail (SYSPROF_IS_MEMPROF_PROFILE (self), 0); - - return self->mode; -} - -void -sysprof_memprof_profile_set_mode (SysprofMemprofProfile *self, - SysprofMemprofMode mode) -{ - g_return_if_fail (SYSPROF_IS_MEMPROF_PROFILE (self)); - - self->mode = mode; -} - -SysprofProfile * -sysprof_memprof_profile_new (void) -{ - return g_object_new (SYSPROF_TYPE_MEMPROF_PROFILE, NULL); -} - -static void -sysprof_memprof_profile_set_reader (SysprofProfile *profile, - SysprofCaptureReader *reader) -{ - SysprofMemprofProfile *self = (SysprofMemprofProfile *)profile; - - g_assert (SYSPROF_IS_MEMPROF_PROFILE (self)); - g_assert (reader != NULL); - - if (reader != self->reader) - { - g_clear_pointer (&self->reader, sysprof_capture_reader_unref); - self->reader = sysprof_capture_reader_ref (reader); - } -} - -static SysprofCaptureCursor * -create_cursor (SysprofCaptureReader *reader) -{ - static SysprofCaptureFrameType types[] = { - SYSPROF_CAPTURE_FRAME_ALLOCATION, - SYSPROF_CAPTURE_FRAME_PROCESS, - }; - SysprofCaptureCursor *cursor; - SysprofCaptureCondition *cond; - - cond = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types); - cursor = sysprof_capture_cursor_new (reader); - sysprof_capture_cursor_add_condition (cursor, cond); - - return cursor; -} - -static bool -all_allocs_foreach_cb (const SysprofCaptureFrame *frame, - void *user_data) -{ - Generate *g = user_data; - - g_assert (frame != NULL); - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_ALLOCATION || - frame->type == SYSPROF_CAPTURE_FRAME_PROCESS); - - if G_UNLIKELY (frame->type == SYSPROF_CAPTURE_FRAME_PROCESS) - { - const SysprofCaptureProcess *pr = (const SysprofCaptureProcess *)frame; - - if (!g_hash_table_contains (g->cmdlines, GINT_TO_POINTER (frame->pid))) - { - g_autofree gchar *cmdline = g_strdup_printf ("[%s]", pr->cmdline); - g_hash_table_insert (g->cmdlines, - GINT_TO_POINTER (frame->pid), - (gchar *)g_string_chunk_insert_const (g->symbols, cmdline)); - } - - return true; - } - - /* Short-circuit if we don't care about this frame */ - if (!sysprof_selection_contains (g->selection, frame->time)) - return true; - - if (frame->type == SYSPROF_CAPTURE_FRAME_ALLOCATION) - { - const SysprofCaptureAllocation *ev = (const SysprofCaptureAllocation *)frame; - - /* Handle memory allocations */ - if (ev->alloc_size > 0) - { - SysprofAddressContext last_context = SYSPROF_ADDRESS_CONTEXT_NONE; - const gchar *cmdline; - StackNode *node; - guint len = 5; - - node = stack_stash_add_trace (g->building, ev->addrs, ev->n_addrs, ev->alloc_size); - - for (const StackNode *iter = node; iter != NULL; iter = iter->parent) - len++; - - if (G_UNLIKELY (g->resolved->len < len)) - g_array_set_size (g->resolved, len); - - len = 0; - - for (const StackNode *iter = node; iter != NULL; iter = iter->parent) - { - SysprofAddressContext context = SYSPROF_ADDRESS_CONTEXT_NONE; - SysprofAddress address = iter->data; - const gchar *symbol = NULL; - - if (sysprof_address_is_context_switch (address, &context)) - { - if (last_context) - symbol = sysprof_address_context_to_string (last_context); - else - symbol = NULL; - - last_context = context; - } - else - { - for (guint i = 0; i < g->resolvers->len; i++) - { - SysprofSymbolResolver *resolver = g_ptr_array_index (g->resolvers, i); - GQuark tag = 0; - gchar *str; - - str = sysprof_symbol_resolver_resolve_with_context (resolver, - frame->time, - frame->pid, - last_context, - address, - &tag); - - if (str != NULL) - { - symbol = g_string_chunk_insert_const (g->symbols, str); - g_free (str); - - if (tag != 0 && !g_hash_table_contains (g->tags, symbol)) - g_hash_table_insert (g->tags, (gchar *)symbol, GSIZE_TO_POINTER (tag)); - - break; - } - } - } - - if (symbol != NULL) - g_array_index (g->resolved, SysprofAddress, len++) = POINTER_TO_U64 (symbol); - } - - if ((cmdline = g_hash_table_lookup (g->cmdlines, GINT_TO_POINTER (frame->pid)))) - g_array_index (g->resolved, guint64, len++) = POINTER_TO_U64 (cmdline); - - g_array_index (g->resolved, guint64, len++) = POINTER_TO_U64 ("[Everything]"); - - stack_stash_add_trace (g->stash, - (gpointer)g->resolved->data, - len, - ev->alloc_size); - } - } - - return true; -} - -static gint -compare_frame_num_reverse (gconstpointer a, - gconstpointer b) -{ - const Alloc *aptr = a; - const Alloc *bptr = b; - - if (aptr->frame_num < bptr->frame_num) - return 1; - else if (aptr->frame_num > bptr->frame_num) - return -1; - else - return 0; -} - - -static gint -compare_alloc (gconstpointer a, - gconstpointer b) -{ - const Alloc *aptr = a; - const Alloc *bptr = b; - - if (aptr->pid < bptr->pid) - return -1; - else if (aptr->pid > bptr->pid) - return 1; - - if (aptr->tid < bptr->tid) - return -1; - else if (aptr->tid > bptr->tid) - return 1; - - if (aptr->time < bptr->time) - return -1; - else if (aptr->time > bptr->time) - return 1; - - if (aptr->addr < bptr->addr) - return -1; - else if (aptr->addr > bptr->addr) - return 1; - else - return 0; -} - -static gint -compare_alloc_pid_addr_time (gconstpointer a, - gconstpointer b) -{ - const Alloc *aptr = a; - const Alloc *bptr = b; - - if (aptr->pid < bptr->pid) - return -1; - else if (aptr->pid > bptr->pid) - return 1; - - if (aptr->addr < bptr->addr) - return -1; - else if (aptr->addr > bptr->addr) - return 1; - - if (aptr->time < bptr->time) - return -1; - else if (aptr->time > bptr->time) - return 1; - else - return 0; -} - -static guint -get_bucket (gint64 size) -{ - if (size <= 32) - return 0; - if (size <= 64) - return 1; - if (size <= 128) - return 2; - if (size <= 256) - return 3; - if (size <= 512) - return 4; - if (size <= 1024) - return 5; - if (size <= 4096) - return 6; - if (size <= 4096*4) - return 7; - if (size <= 4096*8) - return 8; - if (size <= 4096*16) - return 9; - if (size <= 4096*32) - return 10; - if (size <= 4096*64) - return 11; - if (size <= 4096*256) - return 12; - return 13; -} - -static void -summary_worker (Generate *g) -{ - g_autoptr(GArray) allocs = NULL; - SysprofCaptureFrameType type; - SysprofCaptureAddress last_addr = 0; - guint last_bucket = 0; - - g_assert (g != NULL); - g_assert (g->reader != NULL); - - allocs = g_array_new (FALSE, FALSE, sizeof (Alloc)); - - sysprof_capture_reader_reset (g->reader); - - g->stats.by_size[0].bucket = 32; - g->stats.by_size[1].bucket = 64; - g->stats.by_size[2].bucket = 128; - g->stats.by_size[3].bucket = 256; - g->stats.by_size[4].bucket = 512; - g->stats.by_size[5].bucket = 1024; - g->stats.by_size[6].bucket = 4096; - g->stats.by_size[7].bucket = 4096*4; - g->stats.by_size[8].bucket = 4096*8; - g->stats.by_size[9].bucket = 4096*16; - g->stats.by_size[10].bucket = 4096*32; - g->stats.by_size[11].bucket = 4096*64; - g->stats.by_size[12].bucket = 4096*256; - g->stats.by_size[13].bucket = 4096*256; - - while (sysprof_capture_reader_peek_type (g->reader, &type)) - { - if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION) - { - const SysprofCaptureAllocation *ev; - Alloc a; - - if (!(ev = sysprof_capture_reader_read_allocation (g->reader))) - break; - - a.pid = ev->frame.pid; - a.tid = ev->tid; - a.time = ev->frame.time; - a.addr = ev->alloc_addr; - a.size = ev->alloc_size; - a.frame_num = 0; - - g_array_append_val (allocs, a); - - if (a.size > 0) - g->stats.n_allocs++; - } - else - { - if (!sysprof_capture_reader_skip (g->reader)) - break; - } - } - - g_array_sort (allocs, compare_alloc); - - for (guint i = 0; i < allocs->len; i++) - { - const Alloc *a = &g_array_index (allocs, Alloc, i); - - if (a->size <= 0) - { - if (last_addr == a->addr) - { - g->stats.temp_allocs++; - g->stats.by_size[last_bucket].temp_allocs++; - } - - g->stats.leaked_allocs--; - - last_addr = 0; - last_bucket = 0; - } - else - { - guint b = get_bucket (a->size); - - g->stats.n_allocs++; - g->stats.leaked_allocs++; - g->stats.by_size[b].n_allocs++; - g->stats.by_size[b].allocated += a->size; - - last_addr = a->addr; - last_bucket = b; - } - } -} - -static void -temp_allocs_worker (Generate *g) -{ - g_autoptr(GArray) temp_allocs = NULL; - g_autoptr(GArray) all_allocs = NULL; - StackNode *node; - SysprofCaptureFrameType type; - SysprofCaptureAddress last_addr = 0; - guint64 frame_num = 0; - - g_assert (g != NULL); - g_assert (g->reader != NULL); - - temp_allocs = g_array_new (FALSE, FALSE, sizeof (Alloc)); - all_allocs = g_array_new (FALSE, FALSE, sizeof (Alloc)); - - sysprof_capture_reader_reset (g->reader); - - while (sysprof_capture_reader_peek_type (g->reader, &type)) - { - if G_UNLIKELY (type == SYSPROF_CAPTURE_FRAME_PROCESS) - { - const SysprofCaptureProcess *pr; - - if (!(pr = sysprof_capture_reader_read_process (g->reader))) - break; - - if (!g_hash_table_contains (g->cmdlines, GINT_TO_POINTER (pr->frame.pid))) - { - g_autofree gchar *cmdline = g_strdup_printf ("[%s]", pr->cmdline); - g_hash_table_insert (g->cmdlines, - GINT_TO_POINTER (pr->frame.pid), - (gchar *)g_string_chunk_insert_const (g->symbols, cmdline)); - } - } - else if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION) - { - const SysprofCaptureAllocation *ev; - Alloc a; - - if (!(ev = sysprof_capture_reader_read_allocation (g->reader))) - break; - - frame_num++; - - /* Short-circuit if we don't care about this frame */ - if (!sysprof_selection_contains (g->selection, ev->frame.time)) - continue; - - a.pid = ev->frame.pid; - a.tid = ev->tid; - a.time = ev->frame.time; - a.addr = ev->alloc_addr; - a.size = ev->alloc_size; - a.frame_num = frame_num; - - g_array_append_val (all_allocs, a); - } - else - { - if (!sysprof_capture_reader_skip (g->reader)) - break; - } - } - - /* Ensure items are in order because threads may be writing - * data into larger buffers, which are flushed in whole by - * the event marshalling from control fds. - */ - g_array_sort (all_allocs, compare_alloc); - - for (guint i = 0; i < all_allocs->len; i++) - { - const Alloc *a = &g_array_index (all_allocs, Alloc, i); - - if (a->size <= 0) - { - if (a->addr == last_addr && last_addr) - { - const Alloc *prev = a - 1; - g_array_append_vals (temp_allocs, prev, 1); - } - - last_addr = 0; - } - else - { - last_addr = a->addr; - } - } - - if (temp_allocs->len == 0) - return; - - /* Now sort by frame number so we can walk the reader and get the stack - * for each allocation as we count frames. We can skip frames until we - * get to the matching frame_num for the next alloc. - * - * We sort in reverse so that we can just keep shortening the array as - * we match each frame to save having to keep a secondary position - * variable. - */ - g_array_sort (temp_allocs, compare_frame_num_reverse); - sysprof_capture_reader_reset (g->reader); - frame_num = 0; - while (sysprof_capture_reader_peek_type (g->reader, &type)) - { - if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION) - { - const SysprofCaptureAllocation *ev; - const Alloc *tail; - - if (!(ev = sysprof_capture_reader_read_allocation (g->reader))) - break; - - frame_num++; - - tail = &g_array_index (temp_allocs, Alloc, temp_allocs->len - 1); - - if G_UNLIKELY (tail->frame_num == frame_num) - { - SysprofAddressContext last_context = SYSPROF_ADDRESS_CONTEXT_NONE; - const gchar *cmdline; - guint len = 5; - - node = stack_stash_add_trace (g->building, ev->addrs, ev->n_addrs, ev->alloc_size); - - for (const StackNode *iter = node; iter != NULL; iter = iter->parent) - len++; - - if (G_UNLIKELY (g->resolved->len < len)) - g_array_set_size (g->resolved, len); - - len = 0; - - for (const StackNode *iter = node; iter != NULL; iter = iter->parent) - { - SysprofAddressContext context = SYSPROF_ADDRESS_CONTEXT_NONE; - SysprofAddress address = iter->data; - const gchar *symbol = NULL; - - if (sysprof_address_is_context_switch (address, &context)) - { - if (last_context) - symbol = sysprof_address_context_to_string (last_context); - else - symbol = NULL; - - last_context = context; - } - else - { - for (guint i = 0; i < g->resolvers->len; i++) - { - SysprofSymbolResolver *resolver = g_ptr_array_index (g->resolvers, i); - GQuark tag = 0; - gchar *str; - - str = sysprof_symbol_resolver_resolve_with_context (resolver, - ev->frame.time, - ev->frame.pid, - last_context, - address, - &tag); - - if (str != NULL) - { - symbol = g_string_chunk_insert_const (g->symbols, str); - g_free (str); - - if (tag != 0 && !g_hash_table_contains (g->tags, symbol)) - g_hash_table_insert (g->tags, (gchar *)symbol, GSIZE_TO_POINTER (tag)); - - break; - } - } - } - - if (symbol != NULL) - g_array_index (g->resolved, SysprofAddress, len++) = POINTER_TO_U64 (symbol); - } - - if ((cmdline = g_hash_table_lookup (g->cmdlines, GINT_TO_POINTER (ev->frame.pid)))) - g_array_index (g->resolved, guint64, len++) = POINTER_TO_U64 (cmdline); - - g_array_index (g->resolved, guint64, len++) = POINTER_TO_U64 ("[Everything]"); - - stack_stash_add_trace (g->stash, - (gpointer)g->resolved->data, - len, - ev->alloc_size); - - g_array_set_size (temp_allocs, temp_allocs->len - 1); - - if (temp_allocs->len == 0) - break; - } - } - else - { - if (!sysprof_capture_reader_skip (g->reader)) - break; - } - } -} - -static void -leaked_allocs_worker (Generate *g) -{ - g_autoptr(GArray) leak_allocs = NULL; - g_autoptr(GArray) all_allocs = NULL; - StackNode *node; - SysprofCaptureFrameType type; - guint64 frame_num = 0; - - g_assert (g != NULL); - g_assert (g->reader != NULL); - - leak_allocs = g_array_new (FALSE, FALSE, sizeof (Alloc)); - all_allocs = g_array_new (FALSE, FALSE, sizeof (Alloc)); - - sysprof_capture_reader_reset (g->reader); - - while (sysprof_capture_reader_peek_type (g->reader, &type)) - { - if G_UNLIKELY (type == SYSPROF_CAPTURE_FRAME_PROCESS) - { - const SysprofCaptureProcess *pr; - - if (!(pr = sysprof_capture_reader_read_process (g->reader))) - break; - - if (!g_hash_table_contains (g->cmdlines, GINT_TO_POINTER (pr->frame.pid))) - { - g_autofree gchar *cmdline = g_strdup_printf ("[%s]", pr->cmdline); - g_hash_table_insert (g->cmdlines, - GINT_TO_POINTER (pr->frame.pid), - (gchar *)g_string_chunk_insert_const (g->symbols, cmdline)); - } - } - else if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION) - { - const SysprofCaptureAllocation *ev; - Alloc a; - - if (!(ev = sysprof_capture_reader_read_allocation (g->reader))) - break; - - frame_num++; - - /* Short-circuit if we don't care about this frame */ - if (!sysprof_selection_contains (g->selection, ev->frame.time)) - continue; - - a.pid = ev->frame.pid; - a.tid = ev->tid; - a.time = ev->frame.time; - a.addr = ev->alloc_addr; - a.size = ev->alloc_size; - a.frame_num = frame_num; - - g_array_append_val (all_allocs, a); - } - else - { - if (!sysprof_capture_reader_skip (g->reader)) - break; - } - } - - if (all_allocs->len == 0) - return; - - /* Order so we can find frees right after alloc */ - g_array_sort (all_allocs, compare_alloc_pid_addr_time); - - for (guint i = 0; i < all_allocs->len; i++) - { - const Alloc *a = &g_array_index (all_allocs, Alloc, i); - const Alloc *next; - - /* free()s are <= 0 */ - if (a->size <= 0) - continue; - - if (i + 1 == all_allocs->len) - goto leaked; - - next = &g_array_index (all_allocs, Alloc, i+1); - if (a->addr == next->addr && a->pid == next->pid && next->size <= 0) - continue; - - leaked: - g_array_append_vals (leak_allocs, a, 1); - } - - if (leak_allocs->len == 0) - return; - - /* Now sort by frame number so we can walk the reader and get the stack - * for each allocation as we count frames. We can skip frames until we - * get to the matching frame_num for the next alloc. - * - * We sort in reverse so that we can just keep shortening the array as - * we match each frame to save having to keep a secondary position - * variable. - */ - g_array_sort (leak_allocs, compare_frame_num_reverse); - sysprof_capture_reader_reset (g->reader); - frame_num = 0; - while (sysprof_capture_reader_peek_type (g->reader, &type)) - { - if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION) - { - const SysprofCaptureAllocation *ev; - const Alloc *tail; - - if (!(ev = sysprof_capture_reader_read_allocation (g->reader))) - break; - - frame_num++; - - tail = &g_array_index (leak_allocs, Alloc, leak_allocs->len - 1); - - if G_UNLIKELY (tail->frame_num == frame_num) - { - SysprofAddressContext last_context = SYSPROF_ADDRESS_CONTEXT_NONE; - const gchar *cmdline; - guint len = 5; - - node = stack_stash_add_trace (g->building, ev->addrs, ev->n_addrs, ev->alloc_size); - - for (const StackNode *iter = node; iter != NULL; iter = iter->parent) - len++; - - if (G_UNLIKELY (g->resolved->len < len)) - g_array_set_size (g->resolved, len); - - len = 0; - - for (const StackNode *iter = node; iter != NULL; iter = iter->parent) - { - SysprofAddressContext context = SYSPROF_ADDRESS_CONTEXT_NONE; - SysprofAddress address = iter->data; - const gchar *symbol = NULL; - - if (sysprof_address_is_context_switch (address, &context)) - { - if (last_context) - symbol = sysprof_address_context_to_string (last_context); - else - symbol = NULL; - - last_context = context; - } - else - { - for (guint i = 0; i < g->resolvers->len; i++) - { - SysprofSymbolResolver *resolver = g_ptr_array_index (g->resolvers, i); - GQuark tag = 0; - gchar *str; - - str = sysprof_symbol_resolver_resolve_with_context (resolver, - ev->frame.time, - ev->frame.pid, - last_context, - address, - &tag); - - if (str != NULL) - { - symbol = g_string_chunk_insert_const (g->symbols, str); - g_free (str); - - if (tag != 0 && !g_hash_table_contains (g->tags, symbol)) - g_hash_table_insert (g->tags, (gchar *)symbol, GSIZE_TO_POINTER (tag)); - - break; - } - } - } - - if (symbol != NULL) - g_array_index (g->resolved, SysprofAddress, len++) = POINTER_TO_U64 (symbol); - } - - if ((cmdline = g_hash_table_lookup (g->cmdlines, GINT_TO_POINTER (ev->frame.pid)))) - g_array_index (g->resolved, guint64, len++) = POINTER_TO_U64 (cmdline); - - g_array_index (g->resolved, guint64, len++) = POINTER_TO_U64 ("[Everything]"); - - stack_stash_add_trace (g->stash, - (gpointer)g->resolved->data, - len, - ev->alloc_size); - - g_array_set_size (leak_allocs, leak_allocs->len - 1); - - if (leak_allocs->len == 0) - break; - } - } - else - { - if (!sysprof_capture_reader_skip (g->reader)) - break; - } - } -} - -static void -sysprof_memprof_profile_generate_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - Generate *g = task_data; - - g_assert (G_IS_TASK (task)); - g_assert (g != NULL); - g_assert (g->reader != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - /* Make sure the capture is at the beginning */ - sysprof_capture_reader_reset (g->reader); - - /* Load all our symbol resolvers */ - for (guint i = 0; i < g->resolvers->len; i++) - { - SysprofSymbolResolver *resolver = g_ptr_array_index (g->resolvers, i); - - sysprof_symbol_resolver_load (resolver, g->reader); - sysprof_capture_reader_reset (g->reader); - } - - if (g->mode == SYSPROF_MEMPROF_MODE_ALL_ALLOCS) - { - g_autoptr(SysprofCaptureCursor) cursor = NULL; - - cursor = create_cursor (g->reader); - sysprof_capture_cursor_foreach (cursor, all_allocs_foreach_cb, g); - } - else if (g->mode == SYSPROF_MEMPROF_MODE_TEMP_ALLOCS) - { - temp_allocs_worker (g); - } - else if (g->mode == SYSPROF_MEMPROF_MODE_SUMMARY) - { - summary_worker (g); - } - else if (g->mode == SYSPROF_MEMPROF_MODE_LEAKED_ALLOCS) - { - leaked_allocs_worker (g); - } - - /* Release some data we don't need anymore */ - g_clear_pointer (&g->resolved, g_array_unref); - g_clear_pointer (&g->resolvers, g_ptr_array_unref); - g_clear_pointer (&g->reader, sysprof_capture_reader_unref); - g_clear_pointer (&g->building, stack_stash_unref); - g_clear_pointer (&g->cmdlines, g_hash_table_unref); - g_clear_object (&g->selection); - - g_task_return_boolean (task, TRUE); -} - -static void -sysprof_memprof_profile_generate (SysprofProfile *profile, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SysprofMemprofProfile *self = (SysprofMemprofProfile *)profile; - g_autoptr(GTask) task = NULL; - Generate *g; - - g_assert (SYSPROF_IS_MEMPROF_PROFILE (self)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_memprof_profile_generate); - - if (self->reader == NULL) - { - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_NOT_INITIALIZED, - "No capture reader has been set"); - return; - } - - g = g_slice_new0 (Generate); - g->ref_count = 1; - g->reader = sysprof_capture_reader_copy (self->reader); - g->selection = sysprof_selection_copy (self->selection); - g->cmdlines = g_hash_table_new (NULL, NULL); - g->rax = raxNew (); - g->stash = stack_stash_new (NULL); - g->building = stack_stash_new (NULL); - g->resolvers = g_ptr_array_new_with_free_func (g_object_unref); - g->symbols = g_string_chunk_new (4096*4); - g->tags = g_hash_table_new (g_str_hash, g_str_equal); - g->resolved = g_array_new (FALSE, TRUE, sizeof (guint64)); - g->mode = self->mode; - - g_ptr_array_add (g->resolvers, sysprof_capture_symbol_resolver_new ()); - g_ptr_array_add (g->resolvers, sysprof_kernel_symbol_resolver_new ()); - g_ptr_array_add (g->resolvers, sysprof_elf_symbol_resolver_new ()); - - g_task_set_task_data (task, g, (GDestroyNotify) generate_unref); - g_task_run_in_thread (task, sysprof_memprof_profile_generate_worker); -} - -static gboolean -sysprof_memprof_profile_generate_finish (SysprofProfile *profile, - GAsyncResult *result, - GError **error) -{ - SysprofMemprofProfile *self = (SysprofMemprofProfile *)profile; - - g_assert (SYSPROF_IS_MEMPROF_PROFILE (self)); - g_assert (G_IS_TASK (result)); - - g_clear_pointer (&self->g, generate_unref); - - if (g_task_propagate_boolean (G_TASK (result), error)) - { - Generate *g = g_task_get_task_data (G_TASK (result)); - self->g = generate_ref (g); - return TRUE; - } - - return FALSE; -} - -static void -profile_iface_init (SysprofProfileInterface *iface) -{ - iface->set_reader = sysprof_memprof_profile_set_reader; - iface->generate = sysprof_memprof_profile_generate; - iface->generate_finish = sysprof_memprof_profile_generate_finish; -} - -gpointer -sysprof_memprof_profile_get_native (SysprofMemprofProfile *self) -{ - g_return_val_if_fail (SYSPROF_IS_MEMPROF_PROFILE (self), NULL); - - if (self->g != NULL) - return self->g->rax; - - return NULL; -} - -gpointer -sysprof_memprof_profile_get_stash (SysprofMemprofProfile *self) -{ - g_return_val_if_fail (SYSPROF_IS_MEMPROF_PROFILE (self), NULL); - - if (self->g != NULL) - return self->g->stash; - - return NULL; -} - -gboolean -sysprof_memprof_profile_is_empty (SysprofMemprofProfile *self) -{ - StackNode *root; - - g_return_val_if_fail (SYSPROF_IS_MEMPROF_PROFILE (self), FALSE); - - return (self->g == NULL || - self->g->stash == NULL || - !(root = stack_stash_get_root (self->g->stash)) || - !root->total); -} - -GQuark -sysprof_memprof_profile_get_tag (SysprofMemprofProfile *self, - const gchar *symbol) -{ - g_return_val_if_fail (SYSPROF_IS_MEMPROF_PROFILE (self), 0); - - if (self->g != NULL) - return GPOINTER_TO_SIZE (g_hash_table_lookup (self->g->tags, symbol)); - - return 0; -} - -SysprofProfile * -sysprof_memprof_profile_new_with_selection (SysprofSelection *selection) -{ - return g_object_new (SYSPROF_TYPE_MEMPROF_PROFILE, - "selection", selection, - NULL); -} - -void -sysprof_memprof_profile_get_stats (SysprofMemprofProfile *self, - SysprofMemprofStats *stats) -{ - g_return_if_fail (SYSPROF_IS_MEMPROF_PROFILE (self)); - g_return_if_fail (stats != NULL); - - if (self->g != NULL) - *stats = self->g->stats; - else - memset (stats, 0, sizeof *stats); -} diff --git a/src/libsysprof/sysprof-memprof-profile.h b/src/libsysprof/sysprof-memprof-profile.h deleted file mode 100644 index 28e4d915..00000000 --- a/src/libsysprof/sysprof-memprof-profile.h +++ /dev/null @@ -1,98 +0,0 @@ -/* sysprof-memprof-profile.h - * - * 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include "sysprof-version-macros.h" - -#include "sysprof-profile.h" -#include "sysprof-selection.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_MEMPROF_PROFILE (sysprof_memprof_profile_get_type()) - -/** - * SysprofMemprofMode: - * @SYSPROF_MEMPROF_MODE_SUMMARY: The summary profile - * @SYSPROF_MEMPROF_MODE_ALL_ALLOCS: find all allocations - * @SYSPROF_MEMPROF_MODE_TEMP_ALLOCS: find temporary allocations - * @SYSPROF_MEMPROF_MODE_LEAKED_ALLOCS: find allocation leaks, Since 3.44 - * - * The memprof profile mode. - * - * Since 3.44 @SYSPROF_MEMPROF_MODE_LEAKED_ALLOCS is available - * to find leaked allocations. - */ -typedef enum -{ - SYSPROF_MEMPROF_MODE_SUMMARY = 0, - SYSPROF_MEMPROF_MODE_ALL_ALLOCS = 1, - SYSPROF_MEMPROF_MODE_TEMP_ALLOCS = 2, - SYSPROF_MEMPROF_MODE_LEAKED_ALLOCS = 3, -} SysprofMemprofMode; - -typedef struct -{ - gint64 n_allocs; - gint64 leaked_allocs; - gint64 leaked_allocs_size; - gint64 temp_allocs; - gint64 temp_allocs_size; - struct { - gint64 bucket; - gint64 n_allocs; - gint64 temp_allocs; - gint64 allocated; - } by_size[14]; - /*< private >*/ - gint64 _reserved[32]; -} SysprofMemprofStats; - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofMemprofProfile, sysprof_memprof_profile, SYSPROF, MEMPROF_PROFILE, GObject) - -SYSPROF_AVAILABLE_IN_3_36 -SysprofProfile *sysprof_memprof_profile_new (void); -SYSPROF_AVAILABLE_IN_3_36 -SysprofProfile *sysprof_memprof_profile_new_with_selection (SysprofSelection *selection); -SYSPROF_AVAILABLE_IN_3_36 -void sysprof_memprof_profile_get_stats (SysprofMemprofProfile *self, - SysprofMemprofStats *stats); -SYSPROF_AVAILABLE_IN_3_36 -SysprofMemprofMode sysprof_memprof_profile_get_mode (SysprofMemprofProfile *self); -SYSPROF_AVAILABLE_IN_3_36 -void sysprof_memprof_profile_set_mode (SysprofMemprofProfile *self, - SysprofMemprofMode mode); -SYSPROF_AVAILABLE_IN_3_36 -gpointer sysprof_memprof_profile_get_native (SysprofMemprofProfile *self); -SYSPROF_AVAILABLE_IN_3_36 -gpointer sysprof_memprof_profile_get_stash (SysprofMemprofProfile *self); -SYSPROF_AVAILABLE_IN_3_36 -gboolean sysprof_memprof_profile_is_empty (SysprofMemprofProfile *self); -SYSPROF_AVAILABLE_IN_3_36 -GQuark sysprof_memprof_profile_get_tag (SysprofMemprofProfile *self, - const gchar *symbol); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-memprof-source.c b/src/libsysprof/sysprof-memprof-source.c deleted file mode 100644 index 790ba5ef..00000000 --- a/src/libsysprof/sysprof-memprof-source.c +++ /dev/null @@ -1,87 +0,0 @@ -/* sysprof-memprof-source.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 G_LOG_DOMAIN "sysprof-memprof-source" - -#include "config.h" - -#include "sysprof-memprof-source.h" - -struct _SysprofMemprofSource -{ - GObject parent_instance; -}; - -static void -sysprof_memprof_source_modify_spawn (SysprofSource *source, - SysprofSpawnable *spawnable) -{ - g_assert (SYSPROF_IS_SOURCE (source)); - g_assert (SYSPROF_IS_SPAWNABLE (spawnable)); - - sysprof_spawnable_setenv (spawnable, "G_SLICE", "always-malloc"); - -#ifdef __linux__ - { - static const gchar so_path[] = PACKAGE_LIBDIR"/libsysprof-memory-"API_VERSION_S".so"; - g_autofree gchar *freeme = NULL; - const gchar *ld_preload; - - if (!(ld_preload = sysprof_spawnable_getenv (spawnable, "LD_PRELOAD"))) - sysprof_spawnable_setenv (spawnable, "LD_PRELOAD", so_path); - else - sysprof_spawnable_setenv (spawnable, - "LD_PRELOAD", - (freeme = g_strdup_printf ("%s:%s", so_path, ld_preload))); - } -#endif -} - -static void -sysprof_memprof_source_stop (SysprofSource *source) -{ - sysprof_source_emit_finished (source); -} - -static void -source_iface_init (SysprofSourceInterface *iface) -{ - iface->modify_spawn = sysprof_memprof_source_modify_spawn; - iface->stop = sysprof_memprof_source_stop; -} - -G_DEFINE_TYPE_WITH_CODE (SysprofMemprofSource, sysprof_memprof_source, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init)) - -static void -sysprof_memprof_source_class_init (SysprofMemprofSourceClass *klass) -{ -} - -static void -sysprof_memprof_source_init (SysprofMemprofSource *self) -{ -} - -SysprofSource * -sysprof_memprof_source_new (void) -{ - return g_object_new (SYSPROF_TYPE_MEMPROF_SOURCE, NULL); -} diff --git a/src/libsysprof/sysprof-memprof-source.h b/src/libsysprof/sysprof-memprof-source.h deleted file mode 100644 index cfff995a..00000000 --- a/src/libsysprof/sysprof-memprof-source.h +++ /dev/null @@ -1,35 +0,0 @@ -/* sysprof-memprof-source.h - * - * 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 - */ - -#pragma once - -#include "sysprof-tracefd-source.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_MEMPROF_SOURCE (sysprof_memprof_source_get_type()) - -SYSPROF_AVAILABLE_IN_3_36 -G_DECLARE_FINAL_TYPE (SysprofMemprofSource, sysprof_memprof_source, SYSPROF, MEMPROF_SOURCE, GObject) - -SYSPROF_AVAILABLE_IN_3_36 -SysprofSource *sysprof_memprof_source_new (void); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-mountinfo.c b/src/libsysprof/sysprof-mountinfo.c deleted file mode 100644 index 826ce8d6..00000000 --- a/src/libsysprof/sysprof-mountinfo.c +++ /dev/null @@ -1,352 +0,0 @@ -/* sysprof-mountinfo.c - * - * Copyright 2019 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 G_LOG_DOMAIN "sysprof-mountinfo" - -#include "config.h" - -#include "sysprof-mountinfo.h" - -typedef struct -{ - gchar *device; - gchar *mountpoint; - gchar *subvol; -} Mount; - -typedef struct -{ - gchar *host_path; - gchar *mount_path; -} Mountinfo; - -struct _SysprofMountinfo -{ - GArray *mounts; - GArray *mountinfos; - GHashTable *dircache; -}; - -enum { - COLUMN_MOUNT_ID = 0, - COLUMN_MOUNT_PARENT_ID, - COLUMN_MAJOR_MINOR, - COLUMN_ROOT, - COLUMN_MOUNT_POINT, - COLUMN_MOUNT_OPTIONS, -}; - -static void -mount_clear (gpointer data) -{ - Mount *m = data; - - g_free (m->device); - g_free (m->mountpoint); - g_free (m->subvol); -} - -static void -mountinfo_clear (gpointer data) -{ - Mountinfo *m = data; - - g_free (m->host_path); - g_free (m->mount_path); -} - -SysprofMountinfo * -sysprof_mountinfo_new (void) -{ - SysprofMountinfo *self; - - self = g_slice_new0 (SysprofMountinfo); - self->mounts = g_array_new (FALSE, FALSE, sizeof (Mount)); - g_array_set_clear_func (self->mounts, mount_clear); - self->mountinfos = g_array_new (FALSE, FALSE, sizeof (Mountinfo)); - g_array_set_clear_func (self->mountinfos, mountinfo_clear); - self->dircache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - - return g_steal_pointer (&self); -} - -void -sysprof_mountinfo_free (SysprofMountinfo *self) -{ - g_clear_pointer (&self->mounts, g_array_unref); - g_clear_pointer (&self->mountinfos, g_array_unref); - g_clear_pointer (&self->dircache, g_hash_table_unref); - g_slice_free (SysprofMountinfo, self); -} - -gchar * -sysprof_mountinfo_translate (SysprofMountinfo *self, - const gchar *path) -{ - g_autofree gchar *dir = NULL; - const gchar *translate; - - g_assert (self != NULL); - - if (path == NULL) - return NULL; - - /* First try the dircache by looking up the translated parent - * directory and appending basename to it. - */ - dir = g_path_get_dirname (path); - - if ((translate = g_hash_table_lookup (self->dircache, dir))) - { - g_autofree gchar *name = g_path_get_basename (path); - return g_build_filename (translate, name, NULL); - } - - for (guint i = 0; i < self->mountinfos->len; i++) - { - const Mountinfo *m = &g_array_index (self->mountinfos, Mountinfo, i); - - if (g_str_has_prefix (path, m->mount_path)) - { - gchar *ret; - - ret = g_build_filename (m->host_path, path + strlen (m->mount_path), NULL); - g_hash_table_insert (self->dircache, - g_steal_pointer (&dir), - g_path_get_dirname (ret)); - return ret; - } - } - - return NULL; -} - -static void -decode_space (gchar **str) -{ - /* Replace encoded space "\040" with ' ' */ - if (strstr (*str, "\\040")) - { - g_auto(GStrv) parts = g_strsplit (*str, "\\040", 0); - g_free (*str); - *str = g_strjoinv (" ", parts); - } -} - -void -sysprof_mountinfo_parse_mounts (SysprofMountinfo *self, - const gchar *contents) -{ - g_auto(GStrv) lines = NULL; - - g_assert (self != NULL); - g_assert (self->mounts != NULL); - g_assert (contents != NULL); - - lines = g_strsplit (contents, "\n", 0); - - for (guint i = 0; lines[i]; i++) - { - g_auto(GStrv) parts = g_strsplit (lines[i], " ", 5); - g_autofree char *subvol = NULL; - const char *filesystem; - const char *mountpoint; - const char *device; - const char *options; - Mount m; - - /* Field 0: device - * Field 1: mountpoint - * Field 2: filesystem - * Field 3: Options - * .. Ignored .. - */ - if (g_strv_length (parts) != 5) - continue; - - for (guint j = 0; parts[j]; j++) - decode_space (&parts[j]); - - device = parts[0]; - mountpoint = parts[1]; - filesystem = parts[2]; - options = parts[3]; - - if (g_strcmp0 (filesystem, "btrfs") == 0) - { - g_auto(GStrv) opts = g_strsplit (options, ",", 0); - - for (guint k = 0; opts[k]; k++) - { - if (g_str_has_prefix (opts[k], "subvol=")) - { - subvol = g_strdup (opts[k] + strlen ("subvol=")); - break; - } - } - } - - m.device = g_strdup (device); - m.mountpoint = g_strdup (mountpoint); - m.subvol = g_steal_pointer (&subvol); - - g_array_append_val (self->mounts, m); - } -} - -void -sysprof_mountinfo_reset (SysprofMountinfo *self) -{ - g_assert (self != NULL); - g_assert (self->mountinfos != NULL); - - /* Keep mounts, but release mountinfos */ - if (self->mountinfos->len) - g_array_remove_range (self->mountinfos, 0, self->mountinfos->len); - g_hash_table_remove_all (self->dircache); -} - -static const gchar * -get_device_mount (SysprofMountinfo *self, - const gchar *device) -{ - g_assert (self != NULL); - g_assert (self->mounts != NULL); - g_assert (device != NULL); - - for (guint i = 0; i < self->mounts->len; i++) - { - const Mount *m = &g_array_index (self->mounts, Mount, i); - - if (strcmp (device, m->device) == 0) - return m->mountpoint; - } - - return NULL; -} - -static void -sysprof_mountinfo_parse_mountinfo_line (SysprofMountinfo *self, - const gchar *line) -{ - g_auto(GStrv) parts = NULL; - const gchar *prefix; - const gchar *src; - Mountinfo m; - gsize n_parts; - guint i; - - g_assert (self != NULL); - g_assert (self->mounts != NULL); - g_assert (self->mountinfos != NULL); - - parts = g_strsplit (line, " ", 0); - n_parts = g_strv_length (parts); - - if (n_parts < 10) - return; - - /* The device identifier is the 2nd column after "-" */ - for (i = 5; i < n_parts; i++) - { - if (strcmp (parts[i], "-") == 0) - break; - } - if (i >= n_parts || parts[i][0] != '-' || parts[i+1] == NULL || parts[i+2] == NULL) - return; - - prefix = get_device_mount (self, parts[i+2]); - src = parts[COLUMN_ROOT]; - - /* If this references a subvolume, try to find the mount by matching - * the subvolumne using the "src". This isn't exactly correct, but it's - * good enough to get btrfs stuff working for common installs. - */ - if (g_strcmp0 (parts[8], "btrfs") == 0) - { - const char *subvol = src; - - for (i = 0; i < self->mounts->len; i++) - { - const Mount *mnt = &g_array_index (self->mounts, Mount, i); - - if (g_strcmp0 (mnt->subvol, subvol) == 0) - { - src = mnt->mountpoint; - break; - } - } - } - - while (*src == '/') - src++; - - if (*src == 0) - return; - - if (prefix != NULL) - m.host_path = g_build_filename (prefix, src, NULL); - else - m.host_path = g_strdup (src); - - m.mount_path = g_strdup (parts[COLUMN_MOUNT_POINT]); - g_array_append_val (self->mountinfos, m); -} - -static gint -sort_by_length (gconstpointer a, - gconstpointer b) -{ - const Mountinfo *mpa = a; - const Mountinfo *mpb = b; - gsize alen = strlen (mpa->mount_path); - gsize blen = strlen (mpb->mount_path); - - if (alen > blen) - return -1; - else if (blen > alen) - return 1; - else - return 0; -} - -void -sysprof_mountinfo_parse_mountinfo (SysprofMountinfo *self, - const gchar *contents) -{ - g_auto(GStrv) lines = NULL; - - g_assert (self != NULL); - g_assert (self->mounts != NULL); - g_assert (self->mountinfos != NULL); - - lines = g_strsplit (contents, "\n", 0); - - for (guint i = 0; lines[i]; i++) - sysprof_mountinfo_parse_mountinfo_line (self, lines[i]); - - g_array_sort (self->mountinfos, sort_by_length); - - for (guint i = 0; i < self->mountinfos->len; i++) - { - const Mountinfo *m = &g_array_index (self->mountinfos, Mountinfo, i); - g_print ("MM %s => %s\n", m->host_path, m->mount_path); - } -} diff --git a/src/libsysprof/sysprof-mountinfo.h b/src/libsysprof/sysprof-mountinfo.h deleted file mode 100644 index 85c661e2..00000000 --- a/src/libsysprof/sysprof-mountinfo.h +++ /dev/null @@ -1,41 +0,0 @@ -/* sysprof-mountinfo.h - * - * Copyright 2019 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 - */ - -#pragma once - -#include - -G_BEGIN_DECLS - -typedef struct _SysprofMountinfo SysprofMountinfo; - -SysprofMountinfo *sysprof_mountinfo_new (void); -void sysprof_mountinfo_parse_mounts (SysprofMountinfo *self, - const gchar *contents); -void sysprof_mountinfo_parse_mountinfo (SysprofMountinfo *self, - const gchar *contents); -void sysprof_mountinfo_reset (SysprofMountinfo *self); -gchar *sysprof_mountinfo_translate (SysprofMountinfo *self, - const gchar *path); -void sysprof_mountinfo_free (SysprofMountinfo *self); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofMountinfo, sysprof_mountinfo_free) - -G_END_DECLS diff --git a/src/libsysprof/sysprof-netdev-source.c b/src/libsysprof/sysprof-netdev-source.c deleted file mode 100644 index 149c360d..00000000 --- a/src/libsysprof/sysprof-netdev-source.c +++ /dev/null @@ -1,420 +0,0 @@ -/* sysprof-netdev-source.c - * - * Copyright 2019 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 G_LOG_DOMAIN "sysprof-netdev-source" - -#include "config.h" - -#include -#include -#include -#include -#include -#include - -#include "sysprof-backport-autocleanups.h" -#include "sysprof-line-reader.h" -#include "sysprof-netdev-source.h" -#include "sysprof-helpers.h" - -struct _SysprofNetdevSource -{ - GObject parent_instance; - - SysprofCaptureWriter *writer; - GArray *netdevs; - - /* Combined (all devices) rx/tx counters */ - guint combined_rx_id; - guint combined_tx_id; - - /* FD for /proc/net/dev contents */ - gint netdev_fd; - - /* GSource ID for polling */ - guint poll_source; -}; - -typedef struct -{ - /* Counter IDs */ - guint rx_bytes_id; - guint tx_bytes_id; - gchar iface[32]; - gint64 rx_bytes; - gint64 rx_packets; - gint64 rx_errors; - gint64 rx_dropped; - gint64 rx_fifo; - gint64 rx_frame; - gint64 rx_compressed; - gint64 rx_multicast; - gint64 tx_bytes; - gint64 tx_packets; - gint64 tx_errors; - gint64 tx_dropped; - gint64 tx_fifo; - gint64 tx_collisions; - gint64 tx_carrier; - gint64 tx_compressed; -} Netdev; - -static void source_iface_init (SysprofSourceInterface *); - -G_DEFINE_TYPE_WITH_CODE (SysprofNetdevSource, sysprof_netdev_source, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init)) - -static Netdev * -find_device_by_name (SysprofNetdevSource *self, - const gchar *name) -{ - g_assert (SYSPROF_IS_NETDEV_SOURCE (self)); - g_assert (self->writer != NULL); - g_assert (name != NULL); - - for (guint i = 0; i < self->netdevs->len; i++) - { - Netdev *netdev = &g_array_index (self->netdevs, Netdev, i); - - if (strcmp (name, netdev->iface) == 0) - return netdev; - } - - return NULL; -} - -static Netdev * -register_counters_by_name (SysprofNetdevSource *self, - const gchar *name) -{ - SysprofCaptureCounter ctr[2] = {{{0}}}; - g_autofree gchar *rx = NULL; - g_autofree gchar *tx = NULL; - Netdev nd = {0}; - - g_assert (SYSPROF_IS_NETDEV_SOURCE (self)); - g_assert (name != NULL); - g_assert (self->writer != NULL); - - rx = g_strdup_printf ("RX Bytes (%s)", name); - tx = g_strdup_printf ("TX Bytes (%s)", name); - - nd.rx_bytes_id = sysprof_capture_writer_request_counter (self->writer, 1); - nd.tx_bytes_id = sysprof_capture_writer_request_counter (self->writer, 1); - g_strlcpy (nd.iface, name, sizeof nd.iface); - g_array_append_val (self->netdevs, nd); - - g_strlcpy (ctr[0].category, "Network", sizeof ctr[0].category); - g_strlcpy (ctr[0].name, rx, sizeof ctr[0].name); - g_strlcpy (ctr[0].description, name, sizeof ctr[0].description); - ctr[0].id = nd.rx_bytes_id; - ctr[0].type = SYSPROF_CAPTURE_COUNTER_INT64; - ctr[0].value.v64 = 0; - - g_strlcpy (ctr[1].category, "Network", sizeof ctr[1].category); - g_strlcpy (ctr[1].name, tx, sizeof ctr[1].name); - g_strlcpy (ctr[1].description, name, sizeof ctr[1].description); - ctr[1].id = nd.tx_bytes_id; - ctr[1].type = SYSPROF_CAPTURE_COUNTER_INT64; - ctr[1].value.v64 = 0; - - sysprof_capture_writer_define_counters (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - -1, - ctr, G_N_ELEMENTS (ctr)); - - return &g_array_index (self->netdevs, Netdev, self->netdevs->len - 1); -} - -static gboolean -sysprof_netdev_source_get_is_ready (SysprofSource *source) -{ - return TRUE; -} - -static void -sysprof_netdev_source_prepare (SysprofSource *source) -{ - SysprofNetdevSource *self = (SysprofNetdevSource *)source; - g_autoptr(GError) error = NULL; - SysprofCaptureCounter ctr[2] = {{{0}}}; - - g_assert (SYSPROF_IS_NETDEV_SOURCE (self)); - - self->netdev_fd = g_open ("/proc/net/dev", O_RDONLY, 0); - - if (self->netdev_fd == -1) - { - int errsv = errno; - error = g_error_new (G_FILE_ERROR, - g_file_error_from_errno (errsv), - "%s", - g_strerror (errsv)); - sysprof_source_emit_failed (source, error); - return; - } - - self->combined_rx_id = sysprof_capture_writer_request_counter (self->writer, 1); - self->combined_tx_id = sysprof_capture_writer_request_counter (self->writer, 1); - - g_strlcpy (ctr[0].category, "Network", sizeof ctr[0].category); - g_strlcpy (ctr[0].name, "RX Bytes", sizeof ctr[0].name); - g_strlcpy (ctr[0].description, "Combined", sizeof ctr[0].description); - ctr[0].id = self->combined_rx_id; - ctr[0].type = SYSPROF_CAPTURE_COUNTER_INT64; - ctr[0].value.v64 = 0; - - g_strlcpy (ctr[1].category, "Network", sizeof ctr[1].category); - g_strlcpy (ctr[1].name, "TX Bytes", sizeof ctr[1].name); - g_strlcpy (ctr[1].description, "Combined", sizeof ctr[1].description); - ctr[1].id = self->combined_tx_id; - ctr[1].type = SYSPROF_CAPTURE_COUNTER_INT64; - ctr[1].value.v64 = 0; - - sysprof_capture_writer_define_counters (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - -1, - ctr, G_N_ELEMENTS (ctr)); - - sysprof_source_emit_ready (source); -} - -static gboolean -sysprof_netdev_source_poll_cb (gpointer data) -{ - g_autoptr(SysprofLineReader) reader = NULL; - SysprofNetdevSource *self = data; - g_autoptr(GArray) counters = NULL; - g_autoptr(GArray) values = NULL; - gchar buf[4096*4]; - gint64 combined_rx = 0; - gint64 combined_tx = 0; - gssize len; - gsize line_len; - gchar *line; - - g_assert (SYSPROF_IS_NETDEV_SOURCE (self)); - - if (self->netdev_fd == -1) - { - self->poll_source = 0; - return G_SOURCE_REMOVE; - } - - /* Seek to 0 forces reload of data */ - lseek (self->netdev_fd, 0, SEEK_SET); - - len = read (self->netdev_fd, buf, sizeof buf - 1); - - /* Bail for now unless we read enough data */ - if (len > 0) - buf[len] = 0; - else - return G_SOURCE_CONTINUE; - - counters = g_array_new (FALSE, FALSE, sizeof (guint)); - values = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounterValue)); - reader = sysprof_line_reader_new (buf, len); - -#if 0 -Entries looks like this... ------------------------------------------------------------------------------------------------------------------------------- -Inter-| Receive | Transmit - face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed - lo: 10410 104 0 0 0 0 0 0 10410 104 0 0 0 0 0 0 - eth0:1069675772 4197670 0 0 0 0 0 0 3221945712 3290571 0 0 0 0 0 0 ------------------------------------------------------------------------------------------------------------------------------- -#endif - - /* Skip first two lines */ - for (guint i = 0; i < 2; i++) - { - if (!(line = (gchar *)sysprof_line_reader_next (reader, &line_len))) - return G_SOURCE_CONTINUE; - } - - while ((line = (gchar *)sysprof_line_reader_next (reader, &line_len))) - { - Netdev *nd; - gchar *name; - gchar *ptr = line; - - line[line_len] = 0; - - for (; *ptr && g_ascii_isspace (*ptr); ptr++) { /* Do Nothing */ } - name = ptr; - for (; *ptr && *ptr != ':'; ptr++) { /* Do Nothing */ } - *ptr = 0; - - if (!(nd = find_device_by_name (self, name))) - nd = register_counters_by_name (self, name); - - ptr++; - - sscanf (ptr, - "%"G_GINT64_FORMAT - " %"G_GINT64_FORMAT - " %"G_GINT64_FORMAT - " %"G_GINT64_FORMAT - " %"G_GINT64_FORMAT - " %"G_GINT64_FORMAT - " %"G_GINT64_FORMAT - " %"G_GINT64_FORMAT - " %"G_GINT64_FORMAT - " %"G_GINT64_FORMAT - " %"G_GINT64_FORMAT - " %"G_GINT64_FORMAT - " %"G_GINT64_FORMAT - " %"G_GINT64_FORMAT - " %"G_GINT64_FORMAT - " %"G_GINT64_FORMAT, - &nd->rx_bytes, - &nd->rx_packets, - &nd->rx_errors, - &nd->rx_dropped, - &nd->rx_fifo, - &nd->rx_frame, - &nd->rx_compressed, - &nd->rx_multicast, - &nd->tx_bytes, - &nd->tx_packets, - &nd->tx_errors, - &nd->tx_dropped, - &nd->tx_fifo, - &nd->tx_collisions, - &nd->tx_carrier, - &nd->tx_compressed); - - combined_rx += nd->rx_bytes; - combined_tx += nd->tx_bytes; - - g_array_append_val (counters, nd->rx_bytes_id); - g_array_append_val (values, nd->rx_bytes); - - g_array_append_val (counters, nd->tx_bytes_id); - g_array_append_val (values, nd->tx_bytes); - } - - g_array_append_val (counters, self->combined_rx_id); - g_array_append_val (values, combined_rx); - - g_array_append_val (counters, self->combined_tx_id); - g_array_append_val (values, combined_tx); - - sysprof_capture_writer_set_counters (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - -1, - (const guint *)(gpointer)counters->data, - (const SysprofCaptureCounterValue *)(gpointer)values->data, - counters->len); - - return G_SOURCE_CONTINUE; -} - -static void -sysprof_netdev_source_start (SysprofSource *source) -{ - SysprofNetdevSource *self = (SysprofNetdevSource *)source; - - g_assert (SYSPROF_IS_NETDEV_SOURCE (self)); - - self->poll_source = g_timeout_add (200, sysprof_netdev_source_poll_cb, self); - - /* Poll immediately */ - sysprof_netdev_source_poll_cb (self); -} - -static void -sysprof_netdev_source_stop (SysprofSource *source) -{ - SysprofNetdevSource *self = (SysprofNetdevSource *)source; - - g_assert (SYSPROF_IS_NETDEV_SOURCE (self)); - - /* Poll one last time */ - sysprof_netdev_source_poll_cb (self); - - g_clear_handle_id (&self->poll_source, g_source_remove); - - sysprof_source_emit_finished (source); -} - -static void -sysprof_netdev_source_set_writer (SysprofSource *source, - SysprofCaptureWriter *writer) -{ - SysprofNetdevSource *self = (SysprofNetdevSource *)source; - - g_assert (SYSPROF_IS_NETDEV_SOURCE (self)); - g_assert (writer != NULL); - - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); - self->writer = sysprof_capture_writer_ref (writer); -} - -static void -source_iface_init (SysprofSourceInterface *iface) -{ - iface->get_is_ready = sysprof_netdev_source_get_is_ready; - iface->prepare = sysprof_netdev_source_prepare; - iface->set_writer = sysprof_netdev_source_set_writer; - iface->start = sysprof_netdev_source_start; - iface->stop = sysprof_netdev_source_stop; -} - -static void -sysprof_netdev_source_finalize (GObject *object) -{ - SysprofNetdevSource *self = (SysprofNetdevSource *)object; - - g_clear_pointer (&self->netdevs, g_array_unref); - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); - - if (self->netdev_fd != -1) - { - close (self->netdev_fd); - self->netdev_fd = -1; - } - - G_OBJECT_CLASS (sysprof_netdev_source_parent_class)->finalize (object); -} - -static void -sysprof_netdev_source_class_init (SysprofNetdevSourceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_netdev_source_finalize; -} - -static void -sysprof_netdev_source_init (SysprofNetdevSource *self) -{ - self->netdevs = g_array_new (FALSE, FALSE, sizeof (Netdev)); -} - -SysprofSource * -sysprof_netdev_source_new (void) -{ - return g_object_new (SYSPROF_TYPE_NETDEV_SOURCE, NULL); -} diff --git a/src/libsysprof/sysprof-netdev-source.h b/src/libsysprof/sysprof-netdev-source.h deleted file mode 100644 index 4fcc448a..00000000 --- a/src/libsysprof/sysprof-netdev-source.h +++ /dev/null @@ -1,35 +0,0 @@ -/* sysprof-netdev-source.h - * - * Copyright 2019 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 - */ - -#pragma once - -#include "sysprof-source.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_NETDEV_SOURCE (sysprof_netdev_source_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofNetdevSource, sysprof_netdev_source, SYSPROF, NETDEV_SOURCE, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSource *sysprof_netdev_source_new (void); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-path-resolver.c b/src/libsysprof/sysprof-path-resolver.c deleted file mode 100644 index 7d7d9bee..00000000 --- a/src/libsysprof/sysprof-path-resolver.c +++ /dev/null @@ -1,554 +0,0 @@ -/* sysprof-path-resolver.c - * - * Copyright 2021 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 - */ - -#include "config.h" - -#include - -#include "sysprof-path-resolver.h" - -struct _SysprofPathResolver -{ - GArray *mounts; - GArray *mountpoints; -}; - -typedef struct -{ - /* The path on the host system */ - char *on_host; - - /* The path inside the process domain */ - char *in_process; - - /* The length of @in_process in bytes */ - guint in_process_len; - - /* The depth of the mount (for FUSE overlays) */ - int depth; -} Mountpoint; - -typedef struct -{ - char *device; - char *mountpoint; - char *filesystem; - char *subvolid; - char *subvol; -} Mount; - -typedef struct _st_mountinfo -{ - char *id; - char *parent_id; - char *st_dev; - char *root; - char *mount_point; - char *mount_options; - char *filesystem; - char *mount_source; - char *super_options; -} st_mountinfo; - -static void -clear_st_mountinfo (st_mountinfo *st) -{ - g_free (st->id); - g_free (st->parent_id); - g_free (st->st_dev); - g_free (st->root); - g_free (st->mount_point); - g_free (st->mount_options); - g_free (st->filesystem); - g_free (st->mount_source); - g_free (st->super_options); -} - -G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (st_mountinfo, clear_st_mountinfo) - -static void -clear_mount (Mount *m) -{ - g_clear_pointer (&m->device, g_free); - g_clear_pointer (&m->mountpoint, g_free); - g_clear_pointer (&m->subvolid, g_free); - g_clear_pointer (&m->subvol, g_free); - g_clear_pointer (&m->filesystem, g_free); -} - -static void -clear_mountpoint (Mountpoint *mp) -{ - g_clear_pointer (&mp->on_host, g_free); - g_clear_pointer (&mp->in_process, g_free); -} - -static gboolean -ignore_fs (const char *fs) -{ - static gsize initialized; - static GHashTable *ignored; - - if (g_once_init_enter (&initialized)) - { - ignored = g_hash_table_new (g_str_hash, g_str_equal); - g_hash_table_add (ignored, (char *)"autofs"); - g_hash_table_add (ignored, (char *)"binfmt_misc"); - g_hash_table_add (ignored, (char *)"bpf"); - g_hash_table_add (ignored, (char *)"cgroup"); - g_hash_table_add (ignored, (char *)"cgroup2"); - g_hash_table_add (ignored, (char *)"configfs"); - g_hash_table_add (ignored, (char *)"debugfs"); - g_hash_table_add (ignored, (char *)"devpts"); - g_hash_table_add (ignored, (char *)"devtmpfs"); - g_hash_table_add (ignored, (char *)"efivarfs"); - g_hash_table_add (ignored, (char *)"fusectl"); - g_hash_table_add (ignored, (char *)"hugetlbfs"); - g_hash_table_add (ignored, (char *)"mqueue"); - g_hash_table_add (ignored, (char *)"none"); - g_hash_table_add (ignored, (char *)"portal"); - g_hash_table_add (ignored, (char *)"proc"); - g_hash_table_add (ignored, (char *)"pstore"); - g_hash_table_add (ignored, (char *)"ramfs"); - g_hash_table_add (ignored, (char *)"rpc_pipefs"); - g_hash_table_add (ignored, (char *)"securityfs"); - g_hash_table_add (ignored, (char *)"selinuxfs"); - g_hash_table_add (ignored, (char *)"sunrpc"); - g_hash_table_add (ignored, (char *)"sysfs"); - g_hash_table_add (ignored, (char *)"systemd-1"); - g_hash_table_add (ignored, (char *)"tmpfs"); - g_hash_table_add (ignored, (char *)"tracefs"); - g_once_init_leave (&initialized, (gsize)1); - } - - if (g_str_has_prefix (fs, "fuse.")) - return TRUE; - - return g_hash_table_contains (ignored, fs); -} - -static char * -path_copy_with_trailing_slash (const char *path) -{ - if (g_str_has_suffix (path, "/")) - return g_strdup (path); - else - return g_strdup_printf ("%s/", path); -} - -static void -decode_space (char **str) -{ - /* Replace encoded space "\040" with ' ' */ - if (strstr (*str, "\\040") != NULL) - { - g_auto(GStrv) parts = g_strsplit (*str, "\\040", 0); - g_free (*str); - *str = g_strjoinv (" ", parts); - } -} - -static char * -strdup_decode_space (const char *str) -{ - char *copy = g_strdup (str); - decode_space (©); - return copy; -} - -static gboolean -has_prefix_or_equal (const char *str, - const char *prefix) -{ - return g_str_has_prefix (str, prefix) || strcmp (str, prefix) == 0; -} - -static char * -get_option (const char *options, - const char *option) -{ - g_auto(GStrv) parts = NULL; - - g_assert (option != NULL); - g_assert (g_str_has_suffix (option, "=")); - - if (options == NULL) - return NULL; - - parts = g_strsplit (options, ",", 0); - - for (guint i = 0; parts[i] != NULL; i++) - { - if (g_str_has_prefix (parts[i], option)) - { - const char *ret = parts[i] + strlen (option); - - /* Easier to handle "" as NULL */ - if (*ret == 0) - return NULL; - - return g_strdup (ret); - } - } - - return NULL; -} - -static gboolean -parse_st_mountinfo (st_mountinfo *mi, - const char *line) -{ - g_auto(GStrv) parts = NULL; - guint i; - - g_assert (mi != NULL); - g_assert (line != NULL); - - memset (mi, 0, sizeof *mi); - - parts = g_strsplit (line, " ", 0); - if (g_strv_length (parts) < 10) - return FALSE; - - mi->id = g_strdup (parts[0]); - mi->parent_id = g_strdup (parts[1]); - mi->st_dev = g_strdup (parts[2]); - mi->root = strdup_decode_space (parts[3]); - mi->mount_point = strdup_decode_space (parts[4]); - mi->mount_options = strdup_decode_space (parts[5]); - - for (i = 6; parts[i] != NULL && !g_str_equal (parts[i], "-"); i++) - { - /* Do nothing. We just want to skip until after the optional tags - * section which is finished with " - ". - */ - } - - /* Skip past - if there is anything */ - if (parts[i] == NULL || parts[++i] == NULL) - return TRUE; - - /* Get filesystem if provided */ - mi->filesystem = g_strdup (parts[i++]); - if (parts[i] == NULL) - return TRUE; - - /* Get mount source if provided */ - mi->mount_source = strdup_decode_space (parts[i++]); - if (parts[i] == NULL) - return TRUE; - - /* Get super options if provided */ - mi->super_options = strdup_decode_space (parts[i++]); - if (parts[i] == NULL) - return TRUE; - - /* Perhaps mountinfo will be extended once again ... */ - - return TRUE; -} - -static void -parse_mounts (SysprofPathResolver *self, - const char *mounts) -{ - g_auto(GStrv) lines = NULL; - - g_assert (self != NULL); - g_assert (self->mounts != NULL); - g_assert (mounts != NULL); - - lines = g_strsplit (mounts, "\n", 0); - - for (guint i = 0; lines[i]; i++) - { - g_auto(GStrv) parts = g_strsplit (lines[i], " ", 5); - g_autofree char *subvolid = NULL; - g_autofree char *subvol = NULL; - const char *filesystem; - const char *mountpoint; - const char *device; - const char *options; - Mount m; - - /* Field 0: device - * Field 1: mountpoint - * Field 2: filesystem - * Field 3: Options - * .. Ignored .. - */ - if (g_strv_length (parts) != 5) - continue; - - filesystem = parts[2]; - if (ignore_fs (filesystem)) - continue; - - for (guint j = 0; parts[j]; j++) - decode_space (&parts[j]); - - device = parts[0]; - mountpoint = parts[1]; - options = parts[3]; - - - if (g_strcmp0 (filesystem, "btrfs") == 0) - { - subvolid = get_option (options, "subvolid="); - subvol = get_option (options, "subvol="); - } - - m.device = g_strdup (device); - m.filesystem = g_strdup (filesystem); - m.mountpoint = path_copy_with_trailing_slash (mountpoint); - m.subvolid = g_steal_pointer (&subvolid); - m.subvol = g_steal_pointer (&subvol); - - g_array_append_val (self->mounts, m); - } -} - -static const Mount * -find_mount (SysprofPathResolver *self, - const st_mountinfo *mi) -{ - g_autofree char *subvolid = NULL; - - g_assert (self != NULL); - g_assert (mi != NULL); - - subvolid = get_option (mi->super_options, "subvolid="); - - for (guint i = 0; i < self->mounts->len; i++) - { - const Mount *mount = &g_array_index (self->mounts, Mount, i); - - if (g_strcmp0 (mount->device, mi->mount_source) == 0) - { - /* Sanity check that filesystems match */ - if (g_strcmp0 (mount->filesystem, mi->filesystem) != 0) - continue; - - /* If we have a subvolume (btrfs) make sure they match */ - if (subvolid == NULL || - g_strcmp0 (subvolid, mount->subvolid) == 0) - return mount; - } - } - - return NULL; -} - -static void -parse_mountinfo_line (SysprofPathResolver *self, - const char *line) -{ - g_auto(st_mountinfo) st_mi = {0}; - g_autofree char *subvol = NULL; - const char *path; - const Mount *mount; - Mountpoint mp = {0}; - - g_assert (self != NULL); - g_assert (self->mounts != NULL); - g_assert (self->mountpoints != NULL); - - if (!parse_st_mountinfo (&st_mi, line)) - return; - - if (ignore_fs (st_mi.filesystem)) - return; - - if (!(mount = find_mount (self, &st_mi))) - return; - - subvol = get_option (st_mi.super_options, "subvol="); - path = st_mi.root; - - /* If the mount root has a prefix of the subvolume, then - * subtract that from the path (as we will resolve relative - * to the location where it is mounted via the Mount.mountpoint. - */ - if (subvol != NULL && has_prefix_or_equal (path, subvol)) - { - path += strlen (subvol); - if (*path == 0) - path = NULL; - } - - if (path == NULL) - { - mp.on_host = g_strdup (mount->mountpoint); - } - else - { - while (*path == '/') - path++; - mp.on_host = g_build_filename (mount->mountpoint, path, NULL); - } - - if (g_str_has_suffix (mp.on_host, "/") && - !g_str_has_suffix (st_mi.mount_point, "/")) - mp.in_process = g_build_filename (st_mi.mount_point, "/", NULL); - else - mp.in_process = g_strdup (st_mi.mount_point); - - mp.in_process_len = strlen (mp.in_process); - mp.depth = -1; - - g_array_append_val (self->mountpoints, mp); -} - -static gint -sort_by_length (gconstpointer a, - gconstpointer b) -{ - const Mountpoint *mpa = a; - const Mountpoint *mpb = b; - gsize alen = strlen (mpa->in_process); - gsize blen = strlen (mpb->in_process); - - if (alen > blen) - return -1; - else if (blen > alen) - return 1; - - if (mpa->depth < mpb->depth) - return -1; - else if (mpa->depth > mpb->depth) - return 1; - - return 0; -} - -static void -parse_mountinfo (SysprofPathResolver *self, - const char *mountinfo) -{ - g_auto(GStrv) lines = NULL; - - g_assert (self != NULL); - g_assert (self->mounts != NULL); - g_assert (self->mountpoints != NULL); - g_assert (mountinfo != NULL); - - lines = g_strsplit (mountinfo, "\n", 0); - - for (guint i = 0; lines[i]; i++) - parse_mountinfo_line (self, lines[i]); - - g_array_sort (self->mountpoints, sort_by_length); -} - -SysprofPathResolver * -_sysprof_path_resolver_new (const char *mounts, - const char *mountinfo) -{ - SysprofPathResolver *self; - - self = g_slice_new0 (SysprofPathResolver); - - self->mounts = g_array_new (FALSE, FALSE, sizeof (Mount)); - self->mountpoints = g_array_new (FALSE, FALSE, sizeof (Mountpoint)); - - g_array_set_clear_func (self->mounts, (GDestroyNotify)clear_mount); - g_array_set_clear_func (self->mountpoints, (GDestroyNotify)clear_mountpoint); - - if (mounts != NULL) - parse_mounts (self, mounts); - - if (mountinfo != NULL) - parse_mountinfo (self, mountinfo); - - return self; -} - -void -_sysprof_path_resolver_free (SysprofPathResolver *self) -{ - g_clear_pointer (&self->mountpoints, g_array_unref); - g_clear_pointer (&self->mounts, g_array_unref); - g_slice_free (SysprofPathResolver, self); -} - -void -_sysprof_path_resolver_add_overlay (SysprofPathResolver *self, - const char *in_process, - const char *on_host, - int depth) -{ - Mountpoint mp; - - g_return_if_fail (self != NULL); - g_return_if_fail (in_process != NULL); - g_return_if_fail (on_host != NULL); - - mp.in_process = path_copy_with_trailing_slash (in_process); - mp.in_process_len = strlen (mp.in_process); - mp.on_host = path_copy_with_trailing_slash (on_host); - mp.depth = depth; - - g_array_append_val (self->mountpoints, mp); - g_array_sort (self->mountpoints, sort_by_length); -} - -char * -_sysprof_path_resolver_resolve (SysprofPathResolver *self, - const char *path) -{ - g_return_val_if_fail (self != NULL, NULL); - g_return_val_if_fail (path != NULL, NULL); - - /* TODO: Cache the directory name of @path and use that for followup - * searches like we did in SysprofMountinfo. - */ - - for (guint i = 0; i < self->mountpoints->len; i++) - { - const Mountpoint *mp = &g_array_index (self->mountpoints, Mountpoint, i); - - if (g_str_has_prefix (path, mp->in_process)) - { - g_autofree char *dst = g_build_filename (mp->on_host, - path + mp->in_process_len, - NULL); - - /* If the depth is > -1, then we are dealing with an overlay. We - * unfortunately have to stat() to see if the file exists and then - * skip over this if not. - * - * TODO: This is going to break when we are recording from within - * flatpak as we'll not be able to stat files unless they are - * within the current users home, so system containers would - * be unlilkely to resolve. - */ - if (mp->depth > -1) - { - if (g_file_test (dst, G_FILE_TEST_EXISTS)) - return g_steal_pointer (&dst); - continue; - } - - return g_steal_pointer (&dst); - } - } - - return NULL; -} diff --git a/src/libsysprof/sysprof-path-resolver.h b/src/libsysprof/sysprof-path-resolver.h deleted file mode 100644 index 5267cf96..00000000 --- a/src/libsysprof/sysprof-path-resolver.h +++ /dev/null @@ -1,41 +0,0 @@ -/* sysprof-path-resolver.h - * - * Copyright 2021 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 - */ - -#pragma once - -#include - -G_BEGIN_DECLS - -typedef struct _SysprofPathResolver SysprofPathResolver; - -SysprofPathResolver *_sysprof_path_resolver_new (const char *mounts, - const char *mountinfo); -void _sysprof_path_resolver_add_overlay (SysprofPathResolver *self, - const char *in_process, - const char *on_host, - int depth); -void _sysprof_path_resolver_free (SysprofPathResolver *self); -char *_sysprof_path_resolver_resolve (SysprofPathResolver *self, - const char *path); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofPathResolver, _sysprof_path_resolver_free) - -G_END_DECLS diff --git a/src/libsysprof/sysprof-perf-counter.c b/src/libsysprof/sysprof-perf-counter.c deleted file mode 100644 index 2d0b9f46..00000000 --- a/src/libsysprof/sysprof-perf-counter.c +++ /dev/null @@ -1,504 +0,0 @@ -/* sysprof-perf-counter.c - * - * Copyright 2016-2019 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 - */ - -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, Red Hat, Inc. - * Copyright 2004, 2005, Soeren Sandmann - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include -#include -#ifdef HAVE_STDATOMIC_H -# include -#endif -#include -#include -#include -#include -#include - -#include "sysprof-helpers.h" -#include "sysprof-perf-counter.h" - -/* - * Number of pages to map for the ring buffer. We map one additional buffer - * at the beginning for header information to communicate with perf. - */ -#define N_PAGES 32 - -/* - * This represents a stream coming to us from perf. All SysprofPerfCounterInfo - * share a single GSource used for watching incoming G_IO_IN requests. - * The map is the mmap() zone we are using as a ring buffer for communicating - * with perf. The rest is for managing the ring buffer. - */ -typedef struct -{ - gint fd; - gpointer fdtag; - struct perf_event_mmap_page *map; - guint8 *data; - guint64 tail; - gint cpu; - guint in_callback : 1; -} SysprofPerfCounterInfo; - -struct _SysprofPerfCounter -{ - volatile gint ref_count; - - /* - * If we are should currently be enabled. We allow calling - * multiple times and disabling when we reach zero. - */ - guint enabled; - - /* - * Our main context and source for delivering callbacks. - */ - GMainContext *context; - GSource *source; - - /* - * An array of SysprofPerfCounterInfo, indicating all of our open - * perf stream.s - */ - GPtrArray *info; - - /* - * The callback to execute for every discovered perf event. - */ - SysprofPerfCounterCallback callback; - gpointer callback_data; - GDestroyNotify callback_data_destroy; - - /* - * The number of samples we've recorded. - */ - guint64 n_samples; -}; - -typedef struct -{ - GSource source; - SysprofPerfCounter *counter; -} PerfGSource; - -G_DEFINE_BOXED_TYPE (SysprofPerfCounter, - sysprof_perf_counter, - (GBoxedCopyFunc)sysprof_perf_counter_ref, - (GBoxedFreeFunc)sysprof_perf_counter_unref) - -static gboolean -perf_gsource_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data) -{ - return callback ? callback (user_data) : G_SOURCE_CONTINUE; -} - -static GSourceFuncs source_funcs = { - NULL, NULL, perf_gsource_dispatch, NULL -}; - -static void -sysprof_perf_counter_info_free (SysprofPerfCounterInfo *info) -{ - if (info->map) - { - gsize map_size; - - map_size = N_PAGES * getpagesize () + getpagesize (); - munmap (info->map, map_size); - - info->map = NULL; - info->data = NULL; - } - - if (info->fd != -1) - { - close (info->fd); - info->fd = 0; - } - - g_slice_free (SysprofPerfCounterInfo, info); -} - -static void -sysprof_perf_counter_finalize (SysprofPerfCounter *self) -{ - guint i; - - g_assert (self != NULL); - g_assert (self->ref_count == 0); - - for (i = 0; i < self->info->len; i++) - { - SysprofPerfCounterInfo *info = g_ptr_array_index (self->info, i); - - if (info->fdtag) - g_source_remove_unix_fd (self->source, info->fdtag); - - sysprof_perf_counter_info_free (info); - } - - if (self->callback_data_destroy) - self->callback_data_destroy (self->callback_data); - - g_clear_pointer (&self->source, g_source_destroy); - g_clear_pointer (&self->info, g_ptr_array_unref); - g_clear_pointer (&self->context, g_main_context_unref); - g_slice_free (SysprofPerfCounter, self); -} - -void -sysprof_perf_counter_unref (SysprofPerfCounter *self) -{ - g_return_if_fail (self != NULL); - g_return_if_fail (self->ref_count > 0); - - if (g_atomic_int_dec_and_test (&self->ref_count)) - sysprof_perf_counter_finalize (self); -} - -SysprofPerfCounter * -sysprof_perf_counter_ref (SysprofPerfCounter *self) -{ - g_return_val_if_fail (self != NULL, NULL); - g_return_val_if_fail (self->ref_count > 0, NULL); - - g_atomic_int_inc (&self->ref_count); - - return self; -} - -static void -sysprof_perf_counter_flush (SysprofPerfCounter *self, - SysprofPerfCounterInfo *info) -{ - guint64 head; - guint64 tail; - guint64 n_bytes = N_PAGES * getpagesize (); - guint64 mask = n_bytes - 1; - - g_assert (self != NULL); - g_assert (info != NULL); - - tail = info->tail; - head = info->map->data_head; - -#ifdef HAVE_STDATOMIC_H - atomic_thread_fence (memory_order_acquire); -#elif G_GNUC_CHECK_VERSION(3, 0) - __sync_synchronize (); -#endif - - if (head < tail) - tail = head; - - while ((head - tail) >= sizeof (struct perf_event_header)) - { - g_autofree guint8 *free_me = NULL; - struct perf_event_header *header; - guint8 buffer[4096]; - - /* Note that: - * - * - perf events are a multiple of 64 bits - * - the perf event header is 64 bits - * - the data area is a multiple of 64 bits - * - * which means there will always be space for one header, which means we - * can safely dereference the size field. - */ - header = (struct perf_event_header *)(gpointer)(info->data + (tail & mask)); - - if (header->size > head - tail) - { - /* The kernel did not generate a complete event. - * I don't think that can happen, but we may as well - * be paranoid. - */ - break; - } - - if (info->data + (tail & mask) + header->size > info->data + n_bytes) - { - gint n_before; - gint n_after; - guint8 *b; - - if (header->size > sizeof buffer) - free_me = b = g_malloc (header->size); - else - b = buffer; - - n_after = (tail & mask) + header->size - n_bytes; - n_before = header->size - n_after; - - memcpy (b, info->data + (tail & mask), n_before); - memcpy (b + n_before, info->data, n_after); - - header = (struct perf_event_header *)(gpointer)b; - } - - if (header->type == PERF_RECORD_SAMPLE) - self->n_samples++; - - if (self->callback != NULL) - { - info->in_callback = TRUE; - self->callback ((SysprofPerfCounterEvent *)header, info->cpu, self->callback_data); - info->in_callback = FALSE; - } - - tail += header->size; - } - - info->tail = tail; - -#ifdef HAVE_STDATOMIC_H - atomic_thread_fence (memory_order_seq_cst); -#elif G_GNUC_CHECK_VERSION(3, 0) - __sync_synchronize (); -#endif - - info->map->data_tail = tail; -} - -static gboolean -sysprof_perf_counter_dispatch (gpointer user_data) -{ - SysprofPerfCounter *self = user_data; - - g_assert (self != NULL); - g_assert (self->info != NULL); - - for (guint i = 0; i < self->info->len; i++) - { - SysprofPerfCounterInfo *info = g_ptr_array_index (self->info, i); - - sysprof_perf_counter_flush (self, info); - } - - return G_SOURCE_CONTINUE; -} - -static void -sysprof_perf_counter_enable_info (SysprofPerfCounter *self, - SysprofPerfCounterInfo *info) -{ - g_assert (self != NULL); - g_assert (info != NULL); - - if (0 != ioctl (info->fd, PERF_EVENT_IOC_ENABLE)) - g_warning ("Failed to enable counters"); - - g_source_modify_unix_fd (self->source, info->fdtag, G_IO_IN); -} - -SysprofPerfCounter * -sysprof_perf_counter_new (GMainContext *context) -{ - SysprofPerfCounter *ret; - - if (context == NULL) - context = g_main_context_default (); - - ret = g_slice_new0 (SysprofPerfCounter); - ret->ref_count = 1; - ret->info = g_ptr_array_new (); - ret->context = g_main_context_ref (context); - ret->source = g_source_new (&source_funcs, sizeof (PerfGSource)); - - ((PerfGSource *)ret->source)->counter = ret; - g_source_set_callback (ret->source, sysprof_perf_counter_dispatch, ret, NULL); - g_source_set_name (ret->source, "[perf]"); - g_source_attach (ret->source, context); - - return ret; -} - -void -sysprof_perf_counter_close (SysprofPerfCounter *self, - gint fd) -{ - guint i; - - g_return_if_fail (self != NULL); - g_return_if_fail (fd != -1); - - for (i = 0; i < self->info->len; i++) - { - SysprofPerfCounterInfo *info = g_ptr_array_index (self->info, i); - - if (info->fd == fd) - { - g_ptr_array_remove_index (self->info, i); - if (self->source) - g_source_remove_unix_fd (self->source, info->fdtag); - sysprof_perf_counter_info_free (info); - return; - } - } -} - -static void -sysprof_perf_counter_add_info (SysprofPerfCounter *self, - int fd, - int cpu) -{ - SysprofPerfCounterInfo *info; - guint8 *map; - gsize map_size; - - g_assert (self != NULL); - g_assert (fd != -1); - - map_size = N_PAGES * getpagesize () + getpagesize (); - map = mmap (NULL, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - - if (map == MAP_FAILED) - { - close (fd); - return; - } - - info = g_slice_new0 (SysprofPerfCounterInfo); - info->fd = fd; - info->map = (gpointer)map; - info->data = map + getpagesize (); - info->tail = 0; - info->cpu = cpu; - - g_ptr_array_add (self->info, info); - - info->fdtag = g_source_add_unix_fd (self->source, info->fd, G_IO_ERR); - - if (self->enabled) - sysprof_perf_counter_enable_info (self, info); -} - -void -sysprof_perf_counter_take_fd (SysprofPerfCounter *self, - int fd) -{ - g_return_if_fail (self != NULL); - g_return_if_fail (fd > -1); - - sysprof_perf_counter_add_info (self, fd, -1); -} - -gint -sysprof_perf_counter_open (SysprofPerfCounter *self, - struct perf_event_attr *attr, - GPid pid, - gint cpu, - gint group_fd, - gulong flags) -{ - SysprofHelpers *helpers = sysprof_helpers_get_default (); - gint out_fd = -1; - - g_return_val_if_fail (self != NULL, -1); - g_return_val_if_fail (attr != NULL, -1); - g_return_val_if_fail (cpu >= -1, -1); - g_return_val_if_fail (pid >= -1, -1); - g_return_val_if_fail (group_fd >= -1, -1); - - if (sysprof_helpers_perf_event_open (helpers, attr, pid, cpu, group_fd, flags, NULL, &out_fd, NULL)) - { - sysprof_perf_counter_take_fd (self, out_fd); - return out_fd; - } - - return -1; -} - -void -sysprof_perf_counter_set_callback (SysprofPerfCounter *self, - SysprofPerfCounterCallback callback, - gpointer callback_data, - GDestroyNotify callback_data_destroy) -{ - g_return_if_fail (self != NULL); - - if (self->callback_data_destroy) - self->callback_data_destroy (self->callback_data); - - self->callback = callback; - self->callback_data = callback_data; - self->callback_data_destroy = callback_data_destroy; -} - -void -sysprof_perf_counter_enable (SysprofPerfCounter *self) -{ - g_return_if_fail (self != NULL); - - if (g_atomic_int_add (&self->enabled, 1) == 0) - { - for (guint i = 0; i < self->info->len; i++) - { - SysprofPerfCounterInfo *info = g_ptr_array_index (self->info, i); - - sysprof_perf_counter_enable_info (self, info); - } - } -} - -void -sysprof_perf_counter_disable (SysprofPerfCounter *self) -{ - g_return_if_fail (self != NULL); - - if (g_atomic_int_dec_and_test (&self->enabled)) - { - for (guint i = 0; i < self->info->len; i++) - { - SysprofPerfCounterInfo *info = g_ptr_array_index (self->info, i); - - if (0 != ioctl (info->fd, PERF_EVENT_IOC_DISABLE)) - g_warning ("Failed to disable counters"); - - if (!info->in_callback) - sysprof_perf_counter_flush (self, info); - - g_source_modify_unix_fd (self->source, info->fdtag, G_IO_ERR); - } - } -} diff --git a/src/libsysprof/sysprof-perf-counter.h b/src/libsysprof/sysprof-perf-counter.h deleted file mode 100644 index 410e198c..00000000 --- a/src/libsysprof/sysprof-perf-counter.h +++ /dev/null @@ -1,151 +0,0 @@ -/* sysprof-perf-counter.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include -#include - - -G_BEGIN_DECLS - -/* Structs representing the layouts of perf records returned by the - * kernel. - * - * perf returns variable-layout structs based on the - * perf_event_sample_format selectors in perf_event_attr.sample_type. - * These structs are the particular layouts that sysprof requests. - */ - -#define SYSPROF_TYPE_PERF_COUNTER (sysprof_perf_counter_get_type()) - -typedef struct _SysprofPerfCounter SysprofPerfCounter; - -#pragma pack(push, 1) - -typedef struct -{ - struct perf_event_header header; - guint32 pid; - guint32 ppid; - guint32 tid; - guint32 ptid; - guint64 time; -} SysprofPerfCounterEventFork; - -typedef struct -{ - struct perf_event_header header; - guint32 pid; - guint32 tid; - gchar comm[0]; -} SysprofPerfCounterEventComm; - -typedef struct -{ - struct perf_event_header header; - guint32 pid; - guint32 ppid; - guint32 tid; - guint32 ptid; - guint64 time; -} SysprofPerfCounterEventExit; - -typedef struct -{ - struct perf_event_header header; - guint32 pid; - guint32 tid; - guint64 addr; - guint64 len; - guint64 pgoff; - char filename[0]; -} SysprofPerfCounterEventMmap; - -typedef struct -{ - struct perf_event_header header; - guint64 identifier; - guint64 ip; - guint32 pid; - guint32 tid; - guint64 time; - guint64 n_ips; - guint64 ips[0]; -} SysprofPerfCounterEventCallchain; - -typedef struct -{ - struct perf_event_header header; - guint64 identifier; - guint64 ip; - guint32 pid; - guint32 tid; - guint64 time; - guint32 raw_size; - guchar raw[]; -} SysprofPerfCounterEventTracepoint; - -typedef union -{ - struct perf_event_header header; - guint8 raw[0]; - SysprofPerfCounterEventFork fork; - SysprofPerfCounterEventComm comm; - SysprofPerfCounterEventExit exit; - SysprofPerfCounterEventMmap mmap; - SysprofPerfCounterEventCallchain callchain; - SysprofPerfCounterEventTracepoint tracepoint; -} SysprofPerfCounterEvent; - -#pragma pack(pop) - -typedef void (*SysprofPerfCounterCallback) (SysprofPerfCounterEvent *event, - guint cpu, - gpointer user_data); - -GType sysprof_perf_counter_get_type (void); -SysprofPerfCounter *sysprof_perf_counter_new (GMainContext *context); -void sysprof_perf_counter_set_callback (SysprofPerfCounter *self, - SysprofPerfCounterCallback callback, - gpointer callback_data, - GDestroyNotify callback_data_destroy); -void sysprof_perf_counter_add_pid (SysprofPerfCounter *self, - GPid pid); -gint sysprof_perf_counter_open (SysprofPerfCounter *self, - struct perf_event_attr *attr, - GPid pid, - gint cpu, - gint group_fd, - gulong flags); -void sysprof_perf_counter_take_fd (SysprofPerfCounter *self, - int fd); -void sysprof_perf_counter_enable (SysprofPerfCounter *self); -void sysprof_perf_counter_disable (SysprofPerfCounter *self); -void sysprof_perf_counter_close (SysprofPerfCounter *self, - gint fd); -SysprofPerfCounter *sysprof_perf_counter_ref (SysprofPerfCounter *self); -void sysprof_perf_counter_unref (SysprofPerfCounter *self); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-perf-source.c b/src/libsysprof/sysprof-perf-source.c deleted file mode 100644 index c8baa36d..00000000 --- a/src/libsysprof/sysprof-perf-source.c +++ /dev/null @@ -1,831 +0,0 @@ -/* sysprof-perf-source.c - * - * Copyright 2016-2019 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 - */ - -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, Red Hat, Inc. - * Copyright 2004, 2005, Soeren Sandmann - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sysprof-clock.h" -#include "sysprof-helpers.h" -#include "sysprof-line-reader.h" -#include "sysprof-perf-counter.h" -#include "sysprof-perf-source.h" - -#define N_WAKEUP_EVENTS 149 - -/* Identifiers for the various tracepoints we might watch for */ -enum SysprofTracepoint -{ - DRM_VBLANK, - DRM_I915_BEGIN, - DRM_I915_END, -}; - -typedef struct -{ - enum SysprofTracepoint tp; - const char *path; - const char **fields; -} SysprofOptionalTracepoint; - -/* Global list of the optional tracepoints we might want to watch. */ -static const SysprofOptionalTracepoint optional_tracepoints[] = { - /* This event fires just after the vblank IRQ handler starts. - * - * Note that on many platforms when nothing is waiting for vblank - * (no pageflips have happened recently, no rendering is - * synchronizing to vblank), the vblank IRQ will get masked off and - * the event won't show up in the timeline. - * - * Also note that when we're in watch-a-single-process mode, we - * won't get the event since it comes in on an IRQ handler, not for - * our pid. - */ - { DRM_VBLANK, "drm/drm_vblank_event", - (const char *[]){ "crtc", "seq", NULL } }, - - /* I915 GPU execution. - * - * These are the wrong events to be watching. We need to use the - * ones under CONFIG_DRM_I915_LOW_LEVEL_TRACEPOINTS instead. - */ -#if 0 - { DRM_I915_BEGIN, "i915/i915_gem_request_add", - (const char *[]){ "ctx", "ring", "seqno", NULL } }, - { DRM_I915_END, "i915/i915_gem_request_retire", - (const char *[]){ "ctx", "ring", "seqno", NULL } }, -#endif -}; - -/* Struct describing tracepoint events. - * - * This should be extended with some sort of union for the describing - * the locations of the relevant fields within the _RAW section of the - * struct perf_event, so we can pick out things like the vblank CRTC - * number and MSC. - */ -typedef struct { - enum SysprofTracepoint tp; - gsize field_offsets[0]; -} SysprofTracepointDesc; - -struct _SysprofPerfSource -{ - GObject parent_instance; - - SysprofCaptureWriter *writer; - SysprofPerfCounter *counter; - GHashTable *pids; - - /* Mapping from perf sample identifiers to SysprofTracepointDesc. */ - GHashTable *tracepoint_event_ids; - - guint running : 1; - guint is_ready : 1; -}; - -static void source_iface_init (SysprofSourceInterface *iface); - -G_DEFINE_TYPE_EXTENDED (SysprofPerfSource, sysprof_perf_source, G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init)) - -enum { - TARGET_EXITED, - N_SIGNALS -}; - -static guint signals [N_SIGNALS]; - -static void -sysprof_perf_source_real_target_exited (SysprofPerfSource *self) -{ - g_assert (SYSPROF_IS_PERF_SOURCE (self)); - - sysprof_source_emit_finished (SYSPROF_SOURCE (self)); -} - -static void -sysprof_perf_source_finalize (GObject *object) -{ - SysprofPerfSource *self = (SysprofPerfSource *)object; - - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); - g_clear_pointer (&self->counter, sysprof_perf_counter_unref); - g_clear_pointer (&self->pids, g_hash_table_unref); - g_clear_pointer (&self->tracepoint_event_ids, g_hash_table_unref); - - G_OBJECT_CLASS (sysprof_perf_source_parent_class)->finalize (object); -} - -static void -sysprof_perf_source_class_init (SysprofPerfSourceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_perf_source_finalize; - - signals [TARGET_EXITED] = - g_signal_new_class_handler ("target-exited", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - G_CALLBACK (sysprof_perf_source_real_target_exited), - NULL, NULL, NULL, G_TYPE_NONE, 0); -} - -static void -sysprof_perf_source_init (SysprofPerfSource *self) -{ - self->pids = g_hash_table_new (NULL, NULL); - self->tracepoint_event_ids = g_hash_table_new (NULL, NULL); -} - -static gboolean -do_emit_exited (gpointer data) -{ - g_autoptr(SysprofPerfSource) self = data; - - g_signal_emit (self, signals [TARGET_EXITED], 0); - - return G_SOURCE_REMOVE; -} - -static void -sysprof_perf_source_handle_tracepoint (SysprofPerfSource *self, - gint cpu, - const SysprofPerfCounterEventTracepoint *sample, - SysprofTracepointDesc *tp_desc) -{ - gchar *message = NULL; - - /* Note that field_offsets[] correspond to the - * SysprofOptionalTracepoint->fields[] strings. Yes, this is gross. - */ - switch (tp_desc->tp) - { - case DRM_VBLANK: - message = g_strdup_printf ("crtc=%d, seq=%u", - *(gint *)(gpointer)(sample->raw + tp_desc->field_offsets[0]), - *(guint *)(gpointer)(sample->raw + tp_desc->field_offsets[1])); - - sysprof_capture_writer_add_mark (self->writer, - sample->time, - cpu, - sample->pid, - 0, - "drm", - "vblank", - message); - break; - - case DRM_I915_BEGIN: - case DRM_I915_END: - message = g_strdup_printf ("ctx=%u, ring=%u, seqno=%u", - *(guint *)(gpointer)(sample->raw + tp_desc->field_offsets[0]), - *(guint *)(gpointer)(sample->raw + tp_desc->field_offsets[1]), - *(guint *)(gpointer)(sample->raw + tp_desc->field_offsets[2])); - - sysprof_capture_writer_add_mark (self->writer, - sample->time, - cpu, - sample->pid, - 0, - "drm", - (tp_desc->tp == DRM_I915_BEGIN ? - "i915 gpu begin" : "i915 gpu end"), - message); - break; - - default: - break; - } - - g_free (message); -} - -static void -sysprof_perf_source_handle_callchain (SysprofPerfSource *self, - gint cpu, - const SysprofPerfCounterEventCallchain *sample) -{ - const guint64 *ips; - gint n_ips; - guint64 trace[3]; - - g_assert (SYSPROF_IS_PERF_SOURCE (self)); - g_assert (sample != NULL); - - ips = sample->ips; - n_ips = sample->n_ips; - - if (n_ips == 0) - { - if (sample->header.misc & PERF_RECORD_MISC_KERNEL) - { - trace[0] = PERF_CONTEXT_KERNEL; - trace[1] = sample->ip; - trace[2] = PERF_CONTEXT_USER; - - ips = trace; - n_ips = 3; - } - else - { - trace[0] = PERF_CONTEXT_USER; - trace[1] = sample->ip; - - ips = trace; - n_ips = 2; - } - } - - sysprof_capture_writer_add_sample (self->writer, - sample->time, - cpu, - sample->pid, - sample->tid, - ips, - n_ips); -} - -static inline void -realign (gsize *pos, - gsize align) -{ - *pos = (*pos + align - 1) & ~(align - 1); -} - -static void -sysprof_perf_source_handle_event (SysprofPerfCounterEvent *event, - guint cpu, - gpointer user_data) -{ - SysprofPerfSource *self = user_data; - SysprofTracepointDesc *tp_desc; - gsize offset; - gint64 time; - - g_assert (SYSPROF_IS_PERF_SOURCE (self)); - g_assert (event != NULL); - - switch (event->header.type) - { - case PERF_RECORD_COMM: - offset = strlen (event->comm.comm) + 1; - realign (&offset, sizeof (guint64)); - offset += sizeof (GPid) + sizeof (GPid); - memcpy (&time, event->comm.comm + offset, sizeof time); - - if (event->comm.pid == event->comm.tid) - sysprof_capture_writer_add_process (self->writer, - time, - cpu, - event->comm.pid, - event->comm.comm); - - break; - - case PERF_RECORD_EXIT: - /* Ignore fork exits for now */ - if (event->exit.tid != event->exit.pid) - break; - - sysprof_capture_writer_add_exit (self->writer, - event->exit.time, - cpu, - event->exit.pid); - - if (g_hash_table_contains (self->pids, GINT_TO_POINTER (event->exit.pid))) - { - g_hash_table_remove (self->pids, GINT_TO_POINTER (event->exit.pid)); - - if (self->running && (g_hash_table_size (self->pids) == 0)) - { - self->running = FALSE; - sysprof_perf_counter_disable (self->counter); - g_timeout_add (0, do_emit_exited, g_object_ref (self)); - } - } - - break; - - case PERF_RECORD_FORK: - sysprof_capture_writer_add_fork (self->writer, - event->fork.time, - cpu, - event->fork.ptid, - event->fork.tid); - - /* - * TODO: We should add support for "follow fork" of the GPid if we are - * targetting it. - */ - - break; - - case PERF_RECORD_LOST: - break; - - case PERF_RECORD_MMAP: - offset = strlen (event->mmap.filename) + 1; - realign (&offset, sizeof (guint64)); - offset += sizeof (GPid) + sizeof (GPid); - memcpy (&time, event->mmap.filename + offset, sizeof time); - - sysprof_capture_writer_add_map (self->writer, - time, - cpu, - event->mmap.pid, - event->mmap.addr, - event->mmap.addr + event->mmap.len, - event->mmap.pgoff, - 0, - event->mmap.filename); - - break; - - case PERF_RECORD_READ: - break; - - case PERF_RECORD_SAMPLE: - /* We don't capture IPs with tracepoints, and get _RAW data - * instead. Handle them separately. - */ - g_assert (&event->callchain.identifier == &event->tracepoint.identifier); - tp_desc = g_hash_table_lookup (self->tracepoint_event_ids, - GINT_TO_POINTER (event->callchain.identifier)); - if (tp_desc) - { - sysprof_perf_source_handle_tracepoint (self, cpu, &event->tracepoint, tp_desc); - } - else - { - sysprof_perf_source_handle_callchain (self, cpu, &event->callchain); - } - break; - - case PERF_RECORD_THROTTLE: - case PERF_RECORD_UNTHROTTLE: - default: - break; - } -} - -static gboolean -sysprof_perf_get_tracepoint_config (const char *path, - gint64 *config) -{ - g_autofree gchar *filename = NULL; - g_autofree gchar *contents = NULL; - gsize len; - - filename = g_strdup_printf ("/sys/kernel/debug/tracing/events/%s/id", path); - if (!g_file_get_contents (filename, &contents, &len, NULL)) - return FALSE; - - *config = g_ascii_strtoull (contents, NULL, 10); - - return TRUE; -} - -static gboolean -sysprof_perf_get_tracepoint_fields (SysprofTracepointDesc *tp_desc, - const SysprofOptionalTracepoint *optional_tp, - GError **error) -{ - gchar *filename = NULL; - gchar *contents; - size_t len; - gint i; - filename = g_strdup_printf ("/sys/kernel/debug/tracing/events/%s/format", - optional_tp->path); - if (!filename) - return FALSE; - - if (!g_file_get_contents (filename, &contents, &len, NULL)) - { - g_free (filename); - return FALSE; - } - - g_free (filename); - - /* Look up our fields. Some example strings: - * - * field:unsigned short common_type; offset:0; size:2; signed:0; - * field:int crtc; offset:8; size:4; signed:1; - * field:unsigned int seq; offset:12; size:4; signed:0; - */ - for (i = 0; optional_tp->fields[i] != NULL; i++) - { - gchar *pattern = g_strdup_printf ("%s;\toffset:", optional_tp->fields[i]); - gchar *match; - gint64 offset; - - match = strstr (contents, pattern); - if (!match) - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("Sysprof failed to find field “%s”."), - optional_tp->fields[i]); - g_free (contents); - return FALSE; - } - - offset = g_ascii_strtoll (match + strlen (pattern), - NULL, 0); - if (offset == G_MININT64 && errno != 0) - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("Sysprof failed to parse offset for “%s”."), - optional_tp->fields[i]); - g_free (contents); - return FALSE; - } - - tp_desc->field_offsets[i] = offset; - g_free (pattern); - } - - g_free (contents); - - return TRUE; -} - -/* Adds a perf tracepoint event, if it's available. - * - * These are kernel tracepoints that we want to include in our capture - * when present, but may be kernel version or driver-specific. - */ -static void -sysprof_perf_source_add_optional_tracepoint (SysprofPerfSource *self, - GPid pid, - gint cpu, - const SysprofOptionalTracepoint *optional_tracepoint, - GError **error) -{ - struct perf_event_attr attr = { 0 }; - SysprofTracepointDesc *tp_desc; - gulong flags = 0; - gint fd; - gint64 config; - gint64 id; - int ret; - gint num_fields; - - if (!sysprof_perf_get_tracepoint_config(optional_tracepoint->path, &config)) - return; - - attr.type = PERF_TYPE_TRACEPOINT; - attr.sample_type = PERF_SAMPLE_RAW - | PERF_SAMPLE_IP - | PERF_SAMPLE_TID - | PERF_SAMPLE_IDENTIFIER - | PERF_SAMPLE_RAW - | PERF_SAMPLE_TIME; - attr.config = config; - attr.sample_period = 1; - -#ifdef HAVE_PERF_CLOCKID - attr.clockid = sysprof_clock; - attr.use_clockid = 1; -#endif - - attr.size = sizeof attr; - - fd = sysprof_perf_counter_open (self->counter, &attr, pid, cpu, -1, flags); - - ret = ioctl (fd, PERF_EVENT_IOC_ID, &id); - if (ret != 0) - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("Sysprof failed to get perf_event ID.")); - close(fd); - return; - } - - /* The fields list is NULL-terminated, count how many are there. */ - for (num_fields = 0; optional_tracepoint->fields[num_fields]; num_fields++) - ; - - tp_desc = g_malloc (sizeof (*tp_desc) + - sizeof(*tp_desc->field_offsets) * num_fields); - if (!tp_desc) - { - close(fd); - return; - } - - tp_desc->tp = optional_tracepoint->tp; - - if (!sysprof_perf_get_tracepoint_fields (tp_desc, optional_tracepoint, error)) - { - free(tp_desc); - close(fd); - return; - } - - /* Here's where we should inspect the /format file to determine how - * to pick fields out of the _RAW data. - */ - - /* We're truncating the event ID from 64b to 32 to fit in the hash. - * The event IDs start from 0 at boot, so meh. - */ - g_assert (id <= 0xffffffff); - g_hash_table_insert (self->tracepoint_event_ids, GINT_TO_POINTER (id), tp_desc); -} - -static gboolean -sysprof_perf_source_start_pid (SysprofPerfSource *self, - GPid pid, - GError **error) -{ - struct perf_event_attr attr = { 0 }; - gulong flags = 0; - gint ncpu = g_get_num_processors (); - gint cpu = 0; - gint fd = -1; - - g_assert (SYSPROF_IS_PERF_SOURCE (self)); - - attr.sample_type = PERF_SAMPLE_IP - | PERF_SAMPLE_TID - | PERF_SAMPLE_IDENTIFIER - | PERF_SAMPLE_CALLCHAIN - | PERF_SAMPLE_TIME; - attr.wakeup_events = N_WAKEUP_EVENTS; - attr.disabled = TRUE; - attr.mmap = 1; - attr.comm = 1; - attr.task = 1; - attr.exclude_idle = 1; - attr.sample_id_all = 1; - -#ifdef HAVE_PERF_CLOCKID - attr.clockid = sysprof_clock; - attr.use_clockid = 1; -#endif - - attr.size = sizeof attr; - - if (pid != -1) - { - ncpu = 0; - cpu = -1; - } - - /* Perf won't let us capture on all CPUs on all pids, so we have to - * loop over CPUs if we're not just watching a single pid. - */ - for (; cpu < ncpu; cpu++) - { - attr.type = PERF_TYPE_HARDWARE; - attr.config = PERF_COUNT_HW_CPU_CYCLES; - attr.sample_period = 1200000; - - fd = sysprof_perf_counter_open (self->counter, &attr, pid, cpu, -1, flags); - - if (fd == -1) - { - /* - * We might just not have access to hardware counters, so try to - * gracefully fallback to software counters. - */ - attr.type = PERF_TYPE_SOFTWARE; - attr.config = PERF_COUNT_SW_CPU_CLOCK; - attr.sample_period = 1000000; - - errno = 0; - - fd = sysprof_perf_counter_open (self->counter, &attr, pid, cpu, -1, flags); - - if (fd == -1) - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_FAILED, - _("An error occurred while attempting to access performance counters")); - - sysprof_source_stop (SYSPROF_SOURCE (self)); - - return FALSE; - } - } - - for (guint i = 0; i < G_N_ELEMENTS(optional_tracepoints); i++) - sysprof_perf_source_add_optional_tracepoint (self, pid, cpu, - &optional_tracepoints[i], - error); - } - - return TRUE; -} - -static void -sysprof_perf_source_start (SysprofSource *source) -{ - SysprofPerfSource *self = (SysprofPerfSource *)source; - g_autoptr(GError) error = NULL; - - g_assert (SYSPROF_IS_PERF_SOURCE (self)); - - self->counter = sysprof_perf_counter_new (NULL); - - sysprof_perf_counter_set_callback (self->counter, - sysprof_perf_source_handle_event, - self, NULL); - - if (g_hash_table_size (self->pids) > 0) - { - GHashTableIter iter; - gpointer key; - - g_hash_table_iter_init (&iter, self->pids); - - while (g_hash_table_iter_next (&iter, &key, NULL)) - { - GPid pid = GPOINTER_TO_INT (key); - - if (!sysprof_perf_source_start_pid (self, pid, &error)) - { - sysprof_source_emit_failed (source, error); - return; - } - } - } - else - { - if (!sysprof_perf_source_start_pid (self, -1, &error)) - { - sysprof_source_emit_failed (source, error); - return; - } - } - - self->running = TRUE; - - sysprof_perf_counter_enable (self->counter); -} - -static void -sysprof_perf_source_stop (SysprofSource *source) -{ - SysprofPerfSource *self = (SysprofPerfSource *)source; - - g_assert (SYSPROF_IS_PERF_SOURCE (self)); - - if (self->running) - { - self->running = FALSE; - sysprof_perf_counter_disable (self->counter); - } - - g_clear_pointer (&self->counter, sysprof_perf_counter_unref); - - sysprof_source_emit_finished (source); -} - -static void -sysprof_perf_source_set_writer (SysprofSource *source, - SysprofCaptureWriter *writer) -{ - SysprofPerfSource *self = (SysprofPerfSource *)source; - - g_assert (SYSPROF_IS_PERF_SOURCE (self)); - g_assert (writer != NULL); - - self->writer = sysprof_capture_writer_ref (writer); -} - -static void -sysprof_perf_source_add_pid (SysprofSource *source, - GPid pid) -{ - SysprofPerfSource *self = (SysprofPerfSource *)source; - - g_return_if_fail (SYSPROF_IS_PERF_SOURCE (self)); - g_return_if_fail (pid >= -1); - g_return_if_fail (self->writer == NULL); - - g_hash_table_add (self->pids, GINT_TO_POINTER (pid)); -} - -static void -sysprof_perf_source_auth_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofHelpers *helpers = (SysprofHelpers *)object; - g_autoptr(SysprofPerfSource) self = user_data; - g_autoptr(GError) error = NULL; - - g_assert (SYSPROF_IS_HELPERS (helpers)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (SYSPROF_IS_PERF_SOURCE (self)); - - if (!sysprof_helpers_authorize_finish (helpers, result, &error)) - { - sysprof_source_emit_failed (SYSPROF_SOURCE (self), error); - } - else - { - self->is_ready = TRUE; - sysprof_source_emit_ready (SYSPROF_SOURCE (self)); - } -} - -static void -sysprof_perf_source_prepare (SysprofSource *source) -{ - g_assert (SYSPROF_IS_PERF_SOURCE (source)); - - sysprof_helpers_authorize_async (sysprof_helpers_get_default (), - NULL, - sysprof_perf_source_auth_cb, - g_object_ref (source)); -} - -static gboolean -sysprof_perf_source_get_is_ready (SysprofSource *source) -{ - return SYSPROF_PERF_SOURCE (source)->is_ready; -} - -static void -source_iface_init (SysprofSourceInterface *iface) -{ - iface->start = sysprof_perf_source_start; - iface->stop = sysprof_perf_source_stop; - iface->set_writer = sysprof_perf_source_set_writer; - iface->add_pid = sysprof_perf_source_add_pid; - iface->prepare = sysprof_perf_source_prepare; - iface->get_is_ready = sysprof_perf_source_get_is_ready; -} - -SysprofSource * -sysprof_perf_source_new (void) -{ - return g_object_new (SYSPROF_TYPE_PERF_SOURCE, NULL); -} - -void -sysprof_perf_source_set_target_pid (SysprofPerfSource *self, - GPid pid) -{ - g_return_if_fail (SYSPROF_IS_PERF_SOURCE (self)); - g_return_if_fail (pid >= -1); - - if (pid == -1) - g_hash_table_remove_all (self->pids); - else - sysprof_perf_source_add_pid (SYSPROF_SOURCE (self), pid); -} diff --git a/src/libsysprof/sysprof-perf-source.h b/src/libsysprof/sysprof-perf-source.h deleted file mode 100644 index c6fec8f5..00000000 --- a/src/libsysprof/sysprof-perf-source.h +++ /dev/null @@ -1,43 +0,0 @@ -/* sysprof-perf-source.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include "sysprof-source.h" -#include "sysprof-version-macros.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_PERF_SOURCE (sysprof_perf_source_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofPerfSource, sysprof_perf_source, SYSPROF, PERF_SOURCE, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSource *sysprof_perf_source_new (void); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_perf_source_set_target_pid (SysprofPerfSource *self, - GPid pid); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-podman.c b/src/libsysprof/sysprof-podman.c deleted file mode 100644 index 880bc6cc..00000000 --- a/src/libsysprof/sysprof-podman.c +++ /dev/null @@ -1,318 +0,0 @@ -/* sysprof-podman.c - * - * Copyright 2019 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 G_LOG_DOMAIN "sysprof-podman" - -#include "config.h" - -#include - -#include "sysprof-podman.h" - -static const char *debug_dirs[] = { - "/usr/lib/debug", - "/usr/lib32/debug", - "/usr/lib64/debug", -}; - -void -_sysprof_podman_debug_dirs (GPtrArray *dirs) -{ - g_autofree gchar *base_path = NULL; - g_autoptr(GDir) dir = NULL; - const gchar *name; - - g_assert (dirs != NULL); - - base_path = g_build_filename (g_get_user_data_dir (), - "containers", - "storage", - "overlay", - NULL); - - if (!(dir = g_dir_open (base_path, 0, NULL))) - return; - - while ((name = g_dir_read_name (dir))) - { - for (guint i = 0; i < G_N_ELEMENTS (debug_dirs); i++) - { - g_autofree gchar *debug_path = g_build_filename (base_path, name, "diff", debug_dirs[i], NULL); - if (g_file_test (debug_path, G_FILE_TEST_IS_DIR)) - g_ptr_array_add (dirs, g_steal_pointer (&debug_path)); - } - } -} - -gchar ** -sysprof_podman_debug_dirs (void) -{ - GPtrArray *dirs = g_ptr_array_new (); - _sysprof_podman_debug_dirs (dirs); - g_ptr_array_add (dirs, NULL); - return (gchar **)g_ptr_array_free (dirs, FALSE); -} - -struct _SysprofPodman -{ - JsonParser *containers_parser; - JsonParser *layers_parser; - JsonParser *images_parser; -}; - -void -sysprof_podman_free (SysprofPodman *self) -{ - g_clear_object (&self->containers_parser); - g_clear_object (&self->layers_parser); - g_clear_object (&self->images_parser); - g_slice_free (SysprofPodman, self); -} - -static void -load_containers (SysprofPodman *self) -{ - g_autofree char *path = NULL; - - g_assert (self != NULL); - - path = g_build_filename (g_get_user_data_dir (), - "containers", - "storage", - "overlay-containers", - "containers.json", - NULL); - json_parser_load_from_file (self->containers_parser, path, NULL); -} - -static void -load_layers (SysprofPodman *self) -{ - g_autofree char *path = NULL; - - g_assert (self != NULL); - - path = g_build_filename (g_get_user_data_dir (), - "containers", - "storage", - "overlay-layers", - "layers.json", - NULL); - json_parser_load_from_file (self->layers_parser, path, NULL); -} - -static void -load_images (SysprofPodman *self) -{ - g_autofree char *path = NULL; - - g_assert (self != NULL); - - path = g_build_filename (g_get_user_data_dir (), - "containers", - "storage", - "overlay-images", - "images.json", - NULL); - json_parser_load_from_file (self->images_parser, path, NULL); -} - -SysprofPodman * -sysprof_podman_snapshot_current_user (void) -{ - SysprofPodman *self; - - self = g_slice_new0 (SysprofPodman); - self->containers_parser = json_parser_new (); - self->layers_parser = json_parser_new (); - self->images_parser = json_parser_new (); - - load_containers (self); - load_layers (self); - load_images (self); - - return self; -} - -static const char * -find_image_layer (JsonParser *parser, - const char *image) -{ - JsonNode *root; - JsonArray *ar; - guint n_items; - - g_assert (JSON_IS_PARSER (parser)); - g_assert (image != NULL); - - if (!(root = json_parser_get_root (parser)) || - !JSON_NODE_HOLDS_ARRAY (root) || - !(ar = json_node_get_array (root))) - return NULL; - - n_items = json_array_get_length (ar); - - for (guint i = 0; i < n_items; i++) - { - JsonObject *item = json_array_get_object_element (ar, i); - const char *id; - const char *layer; - - if (item == NULL || - !json_object_has_member (item, "id") || - !json_object_has_member (item, "layer") || - !(id = json_object_get_string_member (item, "id")) || - strcmp (id, image) != 0 || - !(layer = json_object_get_string_member (item, "layer"))) - continue; - - return layer; - } - - return NULL; -} - -static const char * -find_parent_layer (JsonParser *parser, - const char *layer, - GHashTable *seen) -{ - JsonNode *root; - JsonArray *ar; - guint n_items; - - g_assert (JSON_IS_PARSER (parser)); - g_assert (layer != NULL); - g_assert (seen != NULL); - - if (!(root = json_parser_get_root (parser)) || - !JSON_NODE_HOLDS_ARRAY (root) || - !(ar = json_node_get_array (root))) - return NULL; - - n_items = json_array_get_length (ar); - - for (guint i = 0; i < n_items; i++) - { - JsonObject *item = json_array_get_object_element (ar, i); - const char *parent; - const char *id; - - if (item == NULL || - !json_object_has_member (item, "id") || - !json_object_has_member (item, "parent") || - !(id = json_object_get_string_member (item, "id")) || - strcmp (id, layer) != 0 || - !(parent = json_object_get_string_member (item, "parent"))) - continue; - - if (g_hash_table_contains (seen, parent)) - return NULL; - - return parent; - } - - return NULL; -} - -static char * -get_layer_dir (const char *layer) -{ - /* We don't use XDG data dir because this might be in a container - * or flatpak environment that doesn't match. And generally, it's - * always .local. - */ - return g_build_filename (g_get_home_dir (), - ".local", - "share", - "containers", - "storage", - "overlay", - layer, - "diff", - NULL); -} - -gchar ** -sysprof_podman_get_layers (SysprofPodman *self, - const char *container) -{ - const char *layer = NULL; - const char *image = NULL; - GHashTable *layers; - JsonNode *root; - JsonArray *ar; - const char **keys; - char **ret; - guint n_items; - - g_return_val_if_fail (self != NULL, NULL); - g_return_val_if_fail (container != NULL, NULL); - - if (!(root = json_parser_get_root (self->containers_parser)) || - !JSON_NODE_HOLDS_ARRAY (root) || - !(ar = json_node_get_array (root))) - return NULL; - - n_items = json_array_get_length (ar); - - /* First try to locate the "layer" identifier for the container - * in question. - */ - for (guint i = 0; i < n_items; i++) - { - JsonObject *item = json_array_get_object_element (ar, i); - const char *item_layer; - const char *id; - - if (item == NULL || - !(id = json_object_get_string_member (item, "id")) || - strcmp (id, container) != 0 || - !(item_layer = json_object_get_string_member (item, "layer"))) - continue; - - layer = item_layer; - image = json_object_get_string_member (item, "image"); - break; - } - - layers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - /* Add all our known layers starting from current layer */ - do - g_hash_table_add (layers, get_layer_dir (layer)); - while ((layer = find_parent_layer (self->layers_parser, layer, layers))); - - /* If an image was specified, add its layer */ - if ((layer = find_image_layer (self->images_parser, image))) - { - do - g_hash_table_add (layers, get_layer_dir (layer)); - while ((layer = find_parent_layer (self->layers_parser, layer, layers))); - } - - keys = (const char **)g_hash_table_get_keys_as_array (layers, NULL); - ret = g_strdupv ((char **)keys); - - g_hash_table_unref (layers); - g_free (keys); - - return ret; -} diff --git a/src/libsysprof/sysprof-podman.h b/src/libsysprof/sysprof-podman.h deleted file mode 100644 index 053c6259..00000000 --- a/src/libsysprof/sysprof-podman.h +++ /dev/null @@ -1,37 +0,0 @@ -/* sysprof-podman.h - * - * 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 - */ - -#pragma once - -#include - -G_BEGIN_DECLS - -typedef struct _SysprofPodman SysprofPodman; - -gchar **sysprof_podman_debug_dirs (void); -SysprofPodman *sysprof_podman_snapshot_current_user (void); -gchar **sysprof_podman_get_layers (SysprofPodman *self, - const char *container); -void sysprof_podman_free (SysprofPodman *self); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofPodman, sysprof_podman_free) - -G_END_DECLS diff --git a/src/libsysprof/sysprof-polkit-private.h b/src/libsysprof/sysprof-polkit-private.h deleted file mode 100644 index 03b59f3e..00000000 --- a/src/libsysprof/sysprof-polkit-private.h +++ /dev/null @@ -1,39 +0,0 @@ -/* sysprof-polkit-private.h - * - * Copyright 2019 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 - */ - -#pragma once - -#include - -G_BEGIN_DECLS - -G_GNUC_INTERNAL -void _sysprof_polkit_authorize_for_bus_async (GDBusConnection *bus, - const gchar *policy, - GHashTable *details, - gboolean allow_user_interaction, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -G_GNUC_INTERNAL -gboolean _sysprof_polkit_authorize_for_bus_finish (GAsyncResult *result, - GError **error); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-polkit.c b/src/libsysprof/sysprof-polkit.c deleted file mode 100644 index fa787f2a..00000000 --- a/src/libsysprof/sysprof-polkit.c +++ /dev/null @@ -1,177 +0,0 @@ -/* sysprof-polkit.c - * - * Copyright 2019 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 G_LOG_DOMAIN "sysprof-polkit" - -#include "config.h" - -#if HAVE_POLKIT -# include -#endif - -#include "sysprof-polkit-private.h" -#include "sysprof-backport-autocleanups.h" - -#if HAVE_POLKIT -typedef struct -{ - const gchar *policy; - PolkitSubject *subject; - GHashTable *details; - guint allow_user_interaction : 1; -} Authorize; - -static void -authorize_free (gpointer data) -{ - Authorize *auth = data; - - g_clear_object (&auth->subject); - g_clear_pointer (&auth->details, g_hash_table_unref); - g_slice_free (Authorize, auth); -} - -static void -sysprof_polkit_check_authorization_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - PolkitAuthority *authority = (PolkitAuthority *)object; - g_autoptr(PolkitAuthorizationResult) res = NULL; - g_autoptr(GError) error = NULL; - g_autoptr(GTask) task = user_data; - - g_assert (POLKIT_IS_AUTHORITY (authority)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - if (!(res = polkit_authority_check_authorization_finish (authority, result, &error))) - g_task_return_error (task, g_steal_pointer (&error)); - else if (!polkit_authorization_result_get_is_authorized (res)) - g_task_return_new_error (task, - G_IO_ERROR, - G_IO_ERROR_PROXY_AUTH_FAILED, - "Failed to authorize user credentials"); - else - g_task_return_boolean (task, TRUE); -} - -static void -sysprof_polkit_get_authority_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - g_autoptr(PolkitAuthority) authority = NULL; - g_autoptr(PolkitDetails) details = NULL; - g_autoptr(GError) error = NULL; - g_autoptr(GTask) task = user_data; - GCancellable *cancellable; - Authorize *auth; - guint flags = 0; - - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - cancellable = g_task_get_cancellable (task); - auth = g_task_get_task_data (task); - - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - g_assert (auth != NULL); - g_assert (POLKIT_IS_SUBJECT (auth->subject)); - - if (!(authority = polkit_authority_get_finish (result, &error))) - { - g_task_return_error (task, g_steal_pointer (&error)); - return; - } - - if (auth->allow_user_interaction) - flags |= POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION; - - if (auth->details != NULL) - { - GHashTableIter iter; - gpointer k, v; - - details = polkit_details_new (); - g_hash_table_iter_init (&iter, auth->details); - while (g_hash_table_iter_next (&iter, &k, &v)) - polkit_details_insert (details, k, v); - } - - polkit_authority_check_authorization (authority, - auth->subject, - auth->policy, - details, - flags, - cancellable, - sysprof_polkit_check_authorization_cb, - g_steal_pointer (&task)); -} -#endif - -void -_sysprof_polkit_authorize_for_bus_async (GDBusConnection *bus, - const gchar *policy, - GHashTable *details, - gboolean allow_user_interaction, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GTask) task = NULL; -#if HAVE_POLKIT - const gchar *bus_name; - Authorize *auth; -#endif - - g_return_if_fail (G_IS_DBUS_CONNECTION (bus)); - g_return_if_fail (policy != NULL); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (NULL, cancellable, callback, user_data); - g_task_set_source_tag (task, _sysprof_polkit_authorize_for_bus_async); - -#if HAVE_POLKIT - bus_name = g_dbus_connection_get_unique_name (bus); - - auth = g_slice_new0 (Authorize); - auth->subject = polkit_system_bus_name_new (bus_name); - auth->policy = g_intern_string (policy); - auth->details = details ? g_hash_table_ref (details) : NULL; - auth->allow_user_interaction = !!allow_user_interaction; - g_task_set_task_data (task, auth, authorize_free); - - polkit_authority_get_async (cancellable, - sysprof_polkit_get_authority_cb, - g_steal_pointer (&task)); -#else - g_task_return_boolean (task, TRUE); -#endif -} - -gboolean -_sysprof_polkit_authorize_for_bus_finish (GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (G_IS_TASK (result), FALSE); - - return g_task_propagate_boolean (G_TASK (result), error); -} diff --git a/src/libsysprof/sysprof-preload-source.c b/src/libsysprof/sysprof-preload-source.c deleted file mode 100644 index fa04f773..00000000 --- a/src/libsysprof/sysprof-preload-source.c +++ /dev/null @@ -1,153 +0,0 @@ -/* sysprof-preload-source.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 G_LOG_DOMAIN "sysprof-preload-source.h" - -#include "config.h" - -#include "sysprof-preload-source.h" - -struct _SysprofPreloadSource -{ - GObject parent_instance; - gchar *preload; -}; - -enum { - PROP_0, - PROP_PRELOAD, - N_PROPS -}; - -static void -sysprof_preload_source_modify_spawn (SysprofSource *source, - SysprofSpawnable *spawnable) -{ - SysprofPreloadSource *self = (SysprofPreloadSource *)source; - - g_assert (SYSPROF_IS_SOURCE (self)); - g_assert (SYSPROF_IS_SPAWNABLE (spawnable)); - -#ifdef __linux__ - if (self->preload) - { - g_autofree gchar *freeme = NULL; - const gchar *ld_preload; - - if (!(ld_preload = sysprof_spawnable_getenv (spawnable, "LD_PRELOAD"))) - sysprof_spawnable_setenv (spawnable, "LD_PRELOAD", self->preload); - else - sysprof_spawnable_setenv (spawnable, - "LD_PRELOAD", - (freeme = g_strdup_printf ("%s:%s", self->preload, ld_preload))); - } -#endif -} - -static void -sysprof_preload_source_stop (SysprofSource *source) -{ - sysprof_source_emit_finished (source); -} - -static void -source_iface_init (SysprofSourceInterface *iface) -{ - iface->modify_spawn = sysprof_preload_source_modify_spawn; - iface->stop = sysprof_preload_source_stop; -} - -G_DEFINE_TYPE_WITH_CODE (SysprofPreloadSource, sysprof_preload_source, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init)) - -static GParamSpec *properties [N_PROPS]; - -static void -sysprof_preload_source_finalize (GObject *object) -{ - SysprofPreloadSource *self = (SysprofPreloadSource *)object; - - g_clear_pointer (&self->preload, g_free); - - G_OBJECT_CLASS (sysprof_preload_source_parent_class)->finalize (object); -} - -static void -sysprof_preload_source_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofPreloadSource *self = SYSPROF_PRELOAD_SOURCE (object); - - switch (prop_id) - { - case PROP_PRELOAD: - g_value_set_string (value, self->preload); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_preload_source_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofPreloadSource *self = SYSPROF_PRELOAD_SOURCE (object); - - switch (prop_id) - { - case PROP_PRELOAD: - g_free (self->preload); - self->preload = g_value_dup_string (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_preload_source_class_init (SysprofPreloadSourceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_preload_source_finalize; - object_class->get_property = sysprof_preload_source_get_property; - object_class->set_property = sysprof_preload_source_set_property; - - properties [PROP_PRELOAD] = - g_param_spec_string ("preload", - "Preload", - "The preload to load into the process", - NULL, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_preload_source_init (SysprofPreloadSource *self) -{ -} diff --git a/src/libsysprof/sysprof-preload-source.h b/src/libsysprof/sysprof-preload-source.h deleted file mode 100644 index 625ca15f..00000000 --- a/src/libsysprof/sysprof-preload-source.h +++ /dev/null @@ -1,32 +0,0 @@ -/* sysprof-preload-source.h - * - * 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 - */ - -#pragma once - -#include "sysprof-source.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_PRELOAD_SOURCE (sysprof_preload_source_get_type()) - -SYSPROF_AVAILABLE_IN_3_38 -G_DECLARE_FINAL_TYPE (SysprofPreloadSource, sysprof_preload_source, SYSPROF, PRELOAD_SOURCE, GObject) - -G_END_DECLS diff --git a/src/libsysprof/sysprof-private.h b/src/libsysprof/sysprof-private.h deleted file mode 100644 index d9cd7733..00000000 --- a/src/libsysprof/sysprof-private.h +++ /dev/null @@ -1,39 +0,0 @@ -/* sysprof-private.h - * - * Copyright 2019 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 - */ - -#pragma once - -#include - -#include "sysprof.h" -#include "sysprof-kallsyms.h" - -G_BEGIN_DECLS - -typedef GArray SysprofKernelSymbols; - -SysprofKernelSymbols *_sysprof_kernel_symbols_get_shared (void); -SysprofKernelSymbols *_sysprof_kernel_symbols_new_from_kallsyms (SysprofKallsyms *kallsyms); -const SysprofKernelSymbol *_sysprof_kernel_symbols_lookup (const SysprofKernelSymbols *self, - SysprofCaptureAddress address); - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofKernelSymbols, g_array_unref) - -G_END_DECLS diff --git a/src/libsysprof/sysprof-proc-source.c b/src/libsysprof/sysprof-proc-source.c deleted file mode 100644 index ba969e24..00000000 --- a/src/libsysprof/sysprof-proc-source.c +++ /dev/null @@ -1,613 +0,0 @@ -/* sysprof-proc-source.c - * - * Copyright 2016-2019 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 - */ - -/* Sysprof -- Sampling, systemwide CPU profiler - * Copyright 2004, Red Hat, Inc. - * Copyright 2004, 2005, Soeren Sandmann - * - * 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 2 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include "config.h" - -#include -#include -#include - -#include - -#include "sysprof-helpers.h" -#include "sysprof-podman.h" -#include "sysprof-proc-source.h" - -struct _SysprofProcSource -{ - GObject parent_instance; - SysprofCaptureWriter *writer; - GArray *pids; - SysprofPodman *podman; - guint is_ready : 1; -}; - -static void source_iface_init (SysprofSourceInterface *iface); - -G_DEFINE_TYPE_EXTENDED (SysprofProcSource, sysprof_proc_source, G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init)) - -static void -sysprof_proc_source_populate_process (SysprofProcSource *self, - GPid pid, - const gchar *cmdline) -{ - g_assert (SYSPROF_IS_PROC_SOURCE (self)); - g_assert (pid > 0); - - sysprof_capture_writer_add_process (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - pid, - cmdline); -} - -static void -sysprof_proc_source_populate_maps (SysprofProcSource *self, - GPid pid, - const gchar *mapsstr, - gboolean ignore_inode) -{ - g_auto(GStrv) lines = NULL; - - g_assert (SYSPROF_IS_PROC_SOURCE (self)); - g_assert (mapsstr != NULL); - g_assert (pid > 0); - - lines = g_strsplit (mapsstr, "\n", 0); - - for (guint i = 0; lines[i] != NULL; i++) - { - gchar file[512]; - gulong start; - gulong end; - gulong offset; - gulong inode; - gint r; - - r = sscanf (lines[i], - "%lx-%lx %*15s %lx %*x:%*x %lu %511[^\n]", - &start, &end, &offset, &inode, file); - file [sizeof file - 1] = '\0'; - - /* file has a " (deleted)" suffix if it was deleted from disk */ - if (g_str_has_suffix (file, " (deleted)")) - file [strlen (file) - strlen (" (deleted)")] = '\0'; - - if (r != 5) - continue; - - if (ignore_inode || strcmp ("[vdso]", file) == 0) - { - /* - * Søren Sandmann Pedersen says: - * - * For the vdso, the kernel reports 'offset' as the - * the same as the mapping addres. This doesn't make - * any sense to me, so we just zero it here. There - * is code in binfile.c (read_inode) that returns 0 - * for [vdso]. - */ - offset = 0; - inode = 0; - } - - sysprof_capture_writer_add_map (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - pid, - start, - end, - offset, - inode, - file); - } -} - -static gboolean -pid_is_interesting (SysprofProcSource *self, - GPid pid) -{ - if (self->pids == NULL || self->pids->len == 0) - return TRUE; - - for (guint i = 0; i < self->pids->len; i++) - { - if (g_array_index (self->pids, GPid, i) == pid) - return TRUE; - } - - return FALSE; -} - -static void -add_file (SysprofProcSource *self, - int pid, - const char *path, - const char *data) -{ - gsize to_write = strlen (data); - - while (to_write > 0) - { - gsize this_write = MIN (to_write, 4096 * 2); - to_write -= this_write; - sysprof_capture_writer_add_file (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - pid, - path, - to_write == 0, - (const guint8 *)data, - this_write); - data += this_write; - } -} - -static void -sysprof_proc_source_populate_mountinfo (SysprofProcSource *self, - GPid pid, - const char *mountinfo) -{ - g_autofree char *path = NULL; - - g_assert (SYSPROF_IS_PROC_SOURCE (self)); - g_assert (self->writer != NULL); - g_assert (mountinfo != NULL); - - path = g_strdup_printf ("/proc/%d/mountinfo", pid); - add_file (self, pid, path, mountinfo); -} - -static void -sysprof_proc_source_populate_pid_podman (SysprofProcSource *self, - GPid pid, - const char *container) -{ - g_auto(GStrv) layers = NULL; - - g_assert (SYSPROF_IS_PROC_SOURCE (self)); - g_assert (container != NULL); - - if (!(layers = sysprof_podman_get_layers (self->podman, container))) - return; - - for (guint i = 0; layers[i]; i++) - sysprof_capture_writer_add_overlay (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, pid, i, layers[i], "/"); -} - -static void -sysprof_proc_source_populate_overlays (SysprofProcSource *self, - GPid pid, - const char *cgroup) -{ - static GRegex *flatpak; - static GRegex *podman; - - g_autoptr(GMatchInfo) flatpak_match = NULL; - g_autoptr(GMatchInfo) podman_match = NULL; - - g_assert (SYSPROF_IS_PROC_SOURCE (self)); - g_assert (cgroup != NULL); - - if (strcmp (cgroup, "") == 0) - return; - - /* This function tries to discover the podman container that contains the - * process identified on the host as @pid. We can only do anything with this - * if the pids are in containers that the running user controls (so that we - * can actually access the overlays). - * - * This stuff, and I want to emphasize, is a giant hack. Just like containers - * are on Linux. But if we are really careful, we can make this work for the - * one particular use case I care about, which is podman/toolbox on Fedora - * Workstation/Silverblue. - * - * -- Christian - */ - if G_UNLIKELY (podman == NULL) - { - podman = g_regex_new ("libpod-([a-z0-9]{64})\\.scope", G_REGEX_OPTIMIZE, 0, NULL); - g_assert (podman != NULL); - } - - if (flatpak == NULL) - { - flatpak = g_regex_new ("app-flatpak-([a-zA-Z_\\-\\.]+)-[0-9]+\\.scope", G_REGEX_OPTIMIZE, 0, NULL); - g_assert (flatpak != NULL); - } - - if (g_regex_match (podman, cgroup, 0, &podman_match)) - { - SysprofHelpers *helpers = sysprof_helpers_get_default (); - g_autofree char *word = g_match_info_fetch (podman_match, 1); - g_autofree char *path = g_strdup_printf ("/proc/%d/root/run/.containerenv", pid); - g_autofree char *info = NULL; - - sysprof_proc_source_populate_pid_podman (self, pid, word); - - if (sysprof_helpers_get_proc_file (helpers, path, NULL, &info, NULL)) - add_file (self, pid, "/run/.containerenv", info); - } - else if (g_regex_match (flatpak, cgroup, 0, &flatpak_match)) - { - SysprofHelpers *helpers = sysprof_helpers_get_default (); - g_autofree char *word = g_match_info_fetch (flatpak_match, 1); - g_autofree char *path = g_strdup_printf ("/proc/%d/root/.flatpak-info", pid); - g_autofree char *info = NULL; - - if (sysprof_helpers_get_proc_file (helpers, path, NULL, &info, NULL)) - add_file (self, pid, "/.flatpak-info", info); - } -} - -static void -sysprof_proc_source_populate (SysprofProcSource *self, - GVariant *info) -{ - g_autofree gchar *mounts = NULL; - SysprofHelpers *helpers; - gsize n_pids = 0; - - g_assert (SYSPROF_IS_PROC_SOURCE (self)); - g_assert (info != NULL); - g_assert (g_variant_is_of_type (info, G_VARIANT_TYPE ("aa{sv}"))); - - if (self->writer == NULL) - return; - - if (self->podman == NULL) - self->podman = sysprof_podman_snapshot_current_user (); - - helpers = sysprof_helpers_get_default (); - if (!sysprof_helpers_get_proc_file (helpers, "/proc/mounts", NULL, &mounts, NULL)) - return; - - add_file (self, -1, "/proc/mounts", mounts); - - n_pids = g_variant_n_children (info); - for (gsize i = 0; i < n_pids; i++) - { - g_autoptr(GVariant) pidinfo = g_variant_get_child_value (info, i); - GVariantDict dict; - const gchar *cmdline; - const gchar *comm; - const gchar *mountinfo; - const gchar *maps; - const gchar *cgroup; - gboolean ignore_inode; - gint32 pid; - - g_variant_dict_init (&dict, pidinfo); - - if (!g_variant_dict_lookup (&dict, "pid", "i", &pid) || - !pid_is_interesting (self, pid)) - goto skip; - - if (!g_variant_dict_lookup (&dict, "cmdline", "&s", &cmdline)) - cmdline = ""; - - if (!g_variant_dict_lookup (&dict, "comm", "&s", &comm)) - comm = ""; - - if (!g_variant_dict_lookup (&dict, "mountinfo", "&s", &mountinfo)) - mountinfo = ""; - - if (!g_variant_dict_lookup (&dict, "maps", "&s", &maps)) - maps = ""; - - if (!g_variant_dict_lookup (&dict, "cgroup", "&s", &cgroup)) - cgroup = ""; - - /* Ignore inodes from podman/toolbox because they appear - * to always be wrong. We'll have to rely on CRC instead. - */ - ignore_inode = strstr (cgroup, "/libpod-") != NULL; - - sysprof_proc_source_populate_process (self, pid, *cmdline ? cmdline : comm); - sysprof_proc_source_populate_mountinfo (self, pid, mountinfo); - sysprof_proc_source_populate_maps (self, pid, maps, ignore_inode); - sysprof_proc_source_populate_overlays (self, pid, cgroup); - - skip: - g_variant_dict_clear (&dict); - } -} - -static void -sysprof_proc_source_get_process_info_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofHelpers *helpers = (SysprofHelpers *)object; - g_autoptr(SysprofProcSource) self = user_data; - g_autoptr(GVariant) info = NULL; - g_autoptr(GError) error = NULL; - - g_assert (SYSPROF_IS_HELPERS (helpers)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (SYSPROF_IS_PROC_SOURCE (self)); - - if (!sysprof_helpers_get_process_info_finish (helpers, result, &info, &error)) - { - sysprof_source_emit_failed (SYSPROF_SOURCE (self), error); - return; - } - - sysprof_proc_source_populate (self, info); - sysprof_source_emit_finished (SYSPROF_SOURCE (self)); -} - -typedef struct _StreamToCapture -{ - SysprofCaptureWriter *writer; - GInputStream *stream; - guint8 *buf; - gsize buflen; -} StreamToCapture; - -static void -stream_to_capture_free (StreamToCapture *state) -{ - g_clear_pointer (&state->writer, sysprof_capture_writer_unref); - g_clear_pointer (&state->buf, g_free); - g_clear_object (&state->stream); - g_free (state); -} - -static gboolean -stream_to_capture (StreamToCapture *state) -{ - gssize n_read; - - g_assert (state != NULL); - g_assert (state->writer != NULL); - g_assert (state->buf != NULL); - g_assert (state->buflen > 0); - g_assert (G_IS_INPUT_STREAM (state->stream)); - - n_read = g_input_stream_read (state->stream, state->buf, state->buflen, NULL, NULL); - - if (n_read >= 0) - sysprof_capture_writer_add_file (state->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - -1, - "/proc/kallsyms.gz", - n_read == 0, - state->buf, - n_read); - - return n_read > 0; -} - -static void -get_kallsyms_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofHelpers *helpers = (SysprofHelpers *)object; - g_autoptr(SysprofProcSource) self = user_data; - g_autoptr(GZlibCompressor) compressor = NULL; - g_autoptr(GInputStream) base_stream = NULL; - g_autoptr(GInputStream) gz_stream = NULL; - g_autoptr(GError) error = NULL; - g_autofree char *contents = NULL; - StreamToCapture *state; - - g_assert (SYSPROF_IS_HELPERS (helpers)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (SYSPROF_IS_PROC_SOURCE (self)); - - if (!sysprof_helpers_get_proc_file_finish (helpers, result, &contents, &error)) - return; - - base_stream = g_memory_input_stream_new_from_data (g_steal_pointer (&contents), -1, g_free); - compressor = g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, 6); - gz_stream = g_converter_input_stream_new (base_stream, G_CONVERTER (compressor)); - - state = g_new0 (StreamToCapture, 1); - state->stream = g_object_ref (gz_stream); - state->writer = sysprof_capture_writer_ref (self->writer); - state->buflen = 16100; /* 16kb write buffer - framing overhead */ - state->buf = g_malloc (state->buflen); - - g_idle_add_full (G_PRIORITY_HIGH, - (GSourceFunc)stream_to_capture, - state, - (GDestroyNotify)stream_to_capture_free); -} - -static void -sysprof_proc_source_start (SysprofSource *source) -{ - SysprofProcSource *self = (SysprofProcSource *)source; - SysprofHelpers *helpers = sysprof_helpers_get_default (); - - g_assert (SYSPROF_IS_PROC_SOURCE (self)); - g_assert (self->writer != NULL); - - sysprof_helpers_get_process_info_async (helpers, - "pid,maps,mountinfo,cmdline,comm,cgroup", - NULL, - sysprof_proc_source_get_process_info_cb, - g_object_ref (self)); - - /* We must read kallsyms from sysprofd because if we ask for a FD and read - * it back from our current process, it's likely the kernel will censor our - * addresses making them useless. - * - * That means a pretty large (couple MB transfer) over D-Bus which is not - * great but also not the end of the world so long as we do it async. - */ - sysprof_helpers_get_proc_file_async (helpers, - "/proc/kallsyms", - NULL, - get_kallsyms_cb, - g_object_ref (self)); -} - -static void -sysprof_proc_source_stop (SysprofSource *source) -{ - SysprofProcSource *self = (SysprofProcSource *)source; - - g_assert (SYSPROF_IS_PROC_SOURCE (self)); - - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); -} - -static void -sysprof_proc_source_set_writer (SysprofSource *source, - SysprofCaptureWriter *writer) -{ - SysprofProcSource *self = (SysprofProcSource *)source; - - g_assert (SYSPROF_IS_PROC_SOURCE (self)); - g_assert (writer != NULL); - - self->writer = sysprof_capture_writer_ref (writer); -} - -static void -sysprof_proc_source_add_pid (SysprofSource *source, - GPid pid) -{ - SysprofProcSource *self = (SysprofProcSource *)source; - guint i; - - g_assert (SYSPROF_IS_PROC_SOURCE (self)); - g_assert (pid > -1); - - for (i = 0; i < self->pids->len; i++) - { - GPid ele = g_array_index (self->pids, GPid, i); - - if (ele == pid) - return; - } - - g_array_append_val (self->pids, pid); -} - -static void -sysprof_proc_source_auth_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofHelpers *helpers = (SysprofHelpers *)object; - g_autoptr(SysprofProcSource) self = user_data; - g_autoptr(GError) error = NULL; - - g_assert (SYSPROF_IS_HELPERS (helpers)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (SYSPROF_IS_PROC_SOURCE (self)); - - if (!sysprof_helpers_authorize_finish (helpers, result, &error)) - { - sysprof_source_emit_failed (SYSPROF_SOURCE (self), error); - } - else - { - self->is_ready = TRUE; - sysprof_source_emit_ready (SYSPROF_SOURCE (self)); - } -} - -static void -sysprof_proc_source_prepare (SysprofSource *source) -{ - g_assert (SYSPROF_IS_PROC_SOURCE (source)); - - sysprof_helpers_authorize_async (sysprof_helpers_get_default (), - NULL, - sysprof_proc_source_auth_cb, - g_object_ref (source)); -} - -static gboolean -sysprof_proc_source_get_is_ready (SysprofSource *source) -{ - return SYSPROF_PROC_SOURCE (source)->is_ready; -} - -static void -source_iface_init (SysprofSourceInterface *iface) -{ - iface->set_writer = sysprof_proc_source_set_writer; - iface->start = sysprof_proc_source_start; - iface->stop = sysprof_proc_source_stop; - iface->add_pid = sysprof_proc_source_add_pid; - iface->prepare = sysprof_proc_source_prepare; - iface->get_is_ready = sysprof_proc_source_get_is_ready; -} - -static void -sysprof_proc_source_finalize (GObject *object) -{ - SysprofProcSource *self = (SysprofProcSource *)object; - - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); - g_clear_pointer (&self->pids, g_array_unref); - g_clear_pointer (&self->podman, sysprof_podman_free); - - G_OBJECT_CLASS (sysprof_proc_source_parent_class)->finalize (object); -} - -static void -sysprof_proc_source_class_init (SysprofProcSourceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_proc_source_finalize; -} - -static void -sysprof_proc_source_init (SysprofProcSource *self) -{ - self->pids = g_array_new (FALSE, FALSE, sizeof (GPid)); -} - -SysprofSource * -sysprof_proc_source_new (void) -{ - return g_object_new (SYSPROF_TYPE_PROC_SOURCE, NULL); -} diff --git a/src/libsysprof/sysprof-proc-source.h b/src/libsysprof/sysprof-proc-source.h deleted file mode 100644 index f3793672..00000000 --- a/src/libsysprof/sysprof-proc-source.h +++ /dev/null @@ -1,41 +0,0 @@ -/* sysprof-proc-source.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include "sysprof-version-macros.h" - -#include "sysprof-source.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_PROC_SOURCE (sysprof_proc_source_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofProcSource, sysprof_proc_source, SYSPROF, PROC_SOURCE, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSource *sysprof_proc_source_new (void); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-process-model-item.c b/src/libsysprof/sysprof-process-model-item.c deleted file mode 100644 index c46b3c43..00000000 --- a/src/libsysprof/sysprof-process-model-item.c +++ /dev/null @@ -1,255 +0,0 @@ -/* sysprof-process-model-item.c - * - * Copyright 2016-2019 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 G_LOG_DOMAIN "sp-process-model-item" - -#include "config.h" - -#include - -#include "sysprof-process-model-item.h" - -#ifdef __linux__ -# include "sysprof-proc-source.h" -#endif - -struct _SysprofProcessModelItem -{ - GObject parent_instance; - GPid pid; - gchar *command_line; /* Short version (first field) */ - gchar **argv; /* Long version (argv as a strv) */ - guint is_kernel : 1; -}; - -G_DEFINE_TYPE (SysprofProcessModelItem, sysprof_process_model_item, G_TYPE_OBJECT) - -enum { - PROP_0, - PROP_COMMAND_LINE, - PROP_PID, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -static void -sysprof_process_model_item_finalize (GObject *object) -{ - SysprofProcessModelItem *self = (SysprofProcessModelItem *)object; - - g_clear_pointer (&self->command_line, g_free); - g_clear_pointer (&self->argv, g_strfreev); - - G_OBJECT_CLASS (sysprof_process_model_item_parent_class)->finalize (object); -} - -static void -sysprof_process_model_item_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofProcessModelItem *self = SYSPROF_PROCESS_MODEL_ITEM (object); - - switch (prop_id) - { - case PROP_COMMAND_LINE: - g_value_set_string (value, self->command_line); - break; - - case PROP_PID: - g_value_set_int (value, self->pid); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_process_model_item_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofProcessModelItem *self = SYSPROF_PROCESS_MODEL_ITEM (object); - - switch (prop_id) - { - case PROP_COMMAND_LINE: - self->command_line = g_value_dup_string (value); - break; - - case PROP_PID: - self->pid = g_value_get_int (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_process_model_item_class_init (SysprofProcessModelItemClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_process_model_item_finalize; - object_class->get_property = sysprof_process_model_item_get_property; - object_class->set_property = sysprof_process_model_item_set_property; - - properties [PROP_COMMAND_LINE] = - g_param_spec_string ("command-line", - "Command Line", - "Command Line", - NULL, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_PID] = - g_param_spec_int ("pid", - "Pid", - "Pid", - -1, - G_MAXINT, - -1, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_process_model_item_init (SysprofProcessModelItem *self) -{ -} - -SysprofProcessModelItem * -sysprof_process_model_item_new_from_variant (GVariant *info) -{ - SysprofProcessModelItem *ret; - GVariantDict dict; - const gchar *cmdline; - - g_return_val_if_fail (info != NULL, NULL); - g_return_val_if_fail (g_variant_is_of_type (info, G_VARIANT_TYPE_VARDICT), NULL); - - ret = g_object_new (SYSPROF_TYPE_PROCESS_MODEL_ITEM, NULL); - - g_variant_dict_init (&dict, info); - - if (g_variant_dict_lookup (&dict, "cmdline", "&s", &cmdline) && *cmdline) - { - if (g_shell_parse_argv (cmdline, NULL, &ret->argv, NULL)) - ret->command_line = g_strdup (ret->argv[0]); - } - else if (g_variant_dict_lookup (&dict, "comm", "&s", &cmdline)) - { - ret->argv = g_new0 (gchar *, 2); - ret->argv[0] = g_strdup (cmdline); - ret->is_kernel = TRUE; - } - - g_variant_dict_lookup (&dict, "pid", "i", &ret->pid); - g_variant_dict_clear (&dict); - - return g_steal_pointer (&ret); -} - -guint -sysprof_process_model_item_hash (SysprofProcessModelItem *self) -{ - g_return_val_if_fail (SYSPROF_IS_PROCESS_MODEL_ITEM (self), 0); - - return self->pid; -} - -gboolean -sysprof_process_model_item_equal (SysprofProcessModelItem *self, - SysprofProcessModelItem *other) -{ - g_assert (SYSPROF_IS_PROCESS_MODEL_ITEM (self)); - g_assert (SYSPROF_IS_PROCESS_MODEL_ITEM (other)); - - return ((self->pid == other->pid) && - (g_strcmp0 (self->command_line, other->command_line) == 0)); -} - -GPid -sysprof_process_model_item_get_pid (SysprofProcessModelItem *self) -{ - g_return_val_if_fail (SYSPROF_IS_PROCESS_MODEL_ITEM (self), 0); - - return self->pid; -} - -const gchar * -sysprof_process_model_item_get_command_line (SysprofProcessModelItem *self) -{ - g_return_val_if_fail (SYSPROF_IS_PROCESS_MODEL_ITEM (self), NULL); - - return self->command_line; -} - -gboolean -sysprof_process_model_item_is_kernel (SysprofProcessModelItem *self) -{ - g_return_val_if_fail (SYSPROF_IS_PROCESS_MODEL_ITEM (self), FALSE); - - return self->is_kernel; -} - -const gchar * const * -sysprof_process_model_item_get_argv (SysprofProcessModelItem *self) -{ - g_autofree gchar *contents = NULL; - g_autofree gchar *path = NULL; - const gchar *pos; - const gchar *endptr; - GPtrArray *ar; - gsize size = 0; - GPid pid; - - g_return_val_if_fail (SYSPROF_IS_PROCESS_MODEL_ITEM (self), NULL); - - if (self->argv) - return (const gchar * const *)self->argv; - - if ((pid = sysprof_process_model_item_get_pid (self)) < 0) - return NULL; - - path = g_strdup_printf ("/proc/%u/cmdline", (guint)pid); - if (!g_file_get_contents (path, &contents, &size, NULL)) - return NULL; - - ar = g_ptr_array_new (); - - /* Each parameter is followed by \0 */ - for (pos = contents, endptr = contents + size; - pos < endptr; - pos += strlen (pos) + 1) - g_ptr_array_add (ar, g_strdup (pos)); - g_ptr_array_add (ar, NULL); - - g_clear_pointer (&self->argv, g_strfreev); - self->argv = (gchar **)g_ptr_array_free (ar, FALSE); - - return (const gchar * const *)self->argv; -} - diff --git a/src/libsysprof/sysprof-process-model-item.h b/src/libsysprof/sysprof-process-model-item.h deleted file mode 100644 index 2b22ec4a..00000000 --- a/src/libsysprof/sysprof-process-model-item.h +++ /dev/null @@ -1,54 +0,0 @@ -/* sysprof-process-model-item.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include "sysprof-version-macros.h" - -#include - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_PROCESS_MODEL_ITEM (sysprof_process_model_item_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofProcessModelItem, sysprof_process_model_item, SYSPROF, PROCESS_MODEL_ITEM, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofProcessModelItem *sysprof_process_model_item_new_from_variant (GVariant *info); -SYSPROF_AVAILABLE_IN_ALL -guint sysprof_process_model_item_hash (SysprofProcessModelItem *self); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_process_model_item_equal (SysprofProcessModelItem *self, - SysprofProcessModelItem *other); -SYSPROF_AVAILABLE_IN_ALL -GPid sysprof_process_model_item_get_pid (SysprofProcessModelItem *self); -SYSPROF_AVAILABLE_IN_ALL -const gchar *sysprof_process_model_item_get_command_line (SysprofProcessModelItem *self); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_process_model_item_is_kernel (SysprofProcessModelItem *self); -SYSPROF_AVAILABLE_IN_ALL -const gchar * const *sysprof_process_model_item_get_argv (SysprofProcessModelItem *self); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-process-model.c b/src/libsysprof/sysprof-process-model.c deleted file mode 100644 index ec7e676d..00000000 --- a/src/libsysprof/sysprof-process-model.c +++ /dev/null @@ -1,307 +0,0 @@ -/* sysprof-process-model.c - * - * Copyright 2016-2019 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 - */ - -#include "config.h" - -#include - -#include "sysprof-backport-autocleanups.h" -#include "sysprof-helpers.h" -#include "sysprof-process-model.h" -#include "sysprof-process-model-item.h" - -#define QUEUE_RELOAD_TIMEOUT_MSEC 100 - -struct _SysprofProcessModel -{ - GObject parent_instance; - GPtrArray *items; - guint reload_source; - guint no_proxy : 1; -}; - -static void list_model_iface_init (GListModelInterface *iface); - -G_DEFINE_TYPE_EXTENDED (SysprofProcessModel, sysprof_process_model, G_TYPE_OBJECT, 0, - G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init)) - -static void -sysprof_process_model_finalize (GObject *object) -{ - SysprofProcessModel *self = (SysprofProcessModel *)object; - - g_clear_handle_id (&self->reload_source, g_source_remove); - g_clear_pointer (&self->items, g_ptr_array_unref); - - G_OBJECT_CLASS (sysprof_process_model_parent_class)->finalize (object); -} - -static void -sysprof_process_model_class_init (SysprofProcessModelClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_process_model_finalize; -} - -static void -sysprof_process_model_init (SysprofProcessModel *self) -{ - self->items = g_ptr_array_new_with_free_func (g_object_unref); -} - -static guint -find_index (GPtrArray *ar, - GPid pid) -{ - guint i; - - g_assert (ar != NULL); - - for (i = 0; i < ar->len; i++) - { - SysprofProcessModelItem *item = g_ptr_array_index (ar, i); - GPid item_pid = sysprof_process_model_item_get_pid (item); - - g_assert (pid != item_pid); - - if (item_pid > pid) - return i; - } - - return ar->len; -} - -static void -sysprof_process_model_merge_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofProcessModel *self = (SysprofProcessModel *)object; - g_autoptr(GPtrArray) ret = NULL; - g_autoptr(GHashTable) old_hash = NULL; - g_autoptr(GHashTable) new_hash = NULL; - GError *error = NULL; - guint i; - - g_assert (SYSPROF_IS_PROCESS_MODEL (self)); - g_assert (G_IS_TASK (result)); - - ret = g_task_propagate_pointer (G_TASK (result), &error); - - if (ret == NULL) - { - g_warning ("%s", error->message); - g_clear_error (&error); - return; - } - - /* - * TODO: Clearly this could be optimized to walk both arrays at once - * and do a proper 2-way merge. - */ - - old_hash = g_hash_table_new ((GHashFunc)sysprof_process_model_item_hash, - (GEqualFunc)sysprof_process_model_item_equal); - new_hash = g_hash_table_new ((GHashFunc)sysprof_process_model_item_hash, - (GEqualFunc)sysprof_process_model_item_equal); - - for (i = 0; i < self->items->len; i++) - { - SysprofProcessModelItem *item = g_ptr_array_index (self->items, i); - - g_hash_table_insert (old_hash, item, NULL); - } - - for (i = 0; i < ret->len; i++) - { - SysprofProcessModelItem *item = g_ptr_array_index (ret, i); - - g_hash_table_insert (new_hash, item, NULL); - } - - for (i = self->items->len; i > 0; i--) - { - guint index = i - 1; - SysprofProcessModelItem *item = g_ptr_array_index (self->items, index); - - if (!g_hash_table_contains (new_hash, item)) - { - g_ptr_array_remove_index (self->items, index); - g_list_model_items_changed (G_LIST_MODEL (self), index, 1, 0); - } - } - - for (i = 0; i < ret->len; i++) - { - SysprofProcessModelItem *item = g_ptr_array_index (ret, i); - GPid pid; - guint index; - - if (g_hash_table_contains (old_hash, item)) - continue; - - pid = sysprof_process_model_item_get_pid (item); - index = find_index (self->items, pid); - - g_ptr_array_insert (self->items, index, g_object_ref (item)); - g_list_model_items_changed (G_LIST_MODEL (self), index, 0, 1); - } -} - -static gint -compare_by_pid (gconstpointer a, - gconstpointer b) -{ - SysprofProcessModelItem **aitem = (SysprofProcessModelItem **)a; - SysprofProcessModelItem **bitem = (SysprofProcessModelItem **)b; - - return sysprof_process_model_item_get_pid (*aitem) - sysprof_process_model_item_get_pid (*bitem); -} - -static void -sysprof_process_model_reload_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - SysprofProcessModel *self = source_object; - SysprofHelpers *helpers = sysprof_helpers_get_default (); - g_autoptr(GPtrArray) ret = NULL; - g_autoptr(GVariant) info = NULL; - - g_assert (SYSPROF_IS_PROCESS_MODEL (source_object)); - g_assert (G_IS_TASK (task)); - - ret = g_ptr_array_new_with_free_func (g_object_unref); - - if (sysprof_helpers_get_process_info (helpers, "pid,cmdline,comm", self->no_proxy, NULL, &info, NULL)) - { - gsize n_children = g_variant_n_children (info); - - for (gsize i = 0; i < n_children; i++) - { - g_autoptr(GVariant) pidinfo = g_variant_get_child_value (info, i); - g_autoptr(SysprofProcessModelItem) item = sysprof_process_model_item_new_from_variant (pidinfo); - - if (sysprof_process_model_item_is_kernel (item)) - continue; - - g_ptr_array_add (ret, g_steal_pointer (&item)); - } - - g_ptr_array_sort (ret, compare_by_pid); - } - - g_task_return_pointer (task, - g_steal_pointer (&ret), - (GDestroyNotify)g_ptr_array_unref); -} - -static gboolean -sysprof_process_model_do_reload (gpointer user_data) -{ - SysprofProcessModel *self = user_data; - g_autoptr(GTask) task = NULL; - - g_clear_handle_id (&self->reload_source, g_source_remove); - - task = g_task_new (self, NULL, sysprof_process_model_merge_cb, NULL); - g_task_set_priority (task, G_PRIORITY_LOW); - g_task_run_in_thread (task, sysprof_process_model_reload_worker); - - return G_SOURCE_REMOVE; -} - -SysprofProcessModel * -sysprof_process_model_new (void) -{ - return g_object_new (SYSPROF_TYPE_PROCESS_MODEL, NULL); -} - -void -sysprof_process_model_reload (SysprofProcessModel *self) -{ - g_autoptr(GTask) task = NULL; - - g_return_if_fail (SYSPROF_IS_PROCESS_MODEL (self)); - - g_clear_handle_id (&self->reload_source, g_source_remove); - - task = g_task_new (self, NULL, NULL, NULL); - g_task_set_priority (task, G_PRIORITY_LOW); - g_task_run_in_thread_sync (task, sysprof_process_model_reload_worker); - - sysprof_process_model_merge_cb (G_OBJECT (self), G_ASYNC_RESULT (task), NULL); -} - -void -sysprof_process_model_queue_reload (SysprofProcessModel *self) -{ - g_return_if_fail (SYSPROF_IS_PROCESS_MODEL (self)); - - if (self->reload_source == 0) - self->reload_source = g_timeout_add (QUEUE_RELOAD_TIMEOUT_MSEC, - sysprof_process_model_do_reload, - self); -} - -static GType -sysprof_process_model_get_item_type (GListModel *model) -{ - return SYSPROF_TYPE_PROCESS_MODEL_ITEM; -} - -static guint -sysprof_process_model_get_n_items (GListModel *model) -{ - SysprofProcessModel *self = (SysprofProcessModel *)model; - - return self->items->len; -} - -static gpointer -sysprof_process_model_get_item (GListModel *model, - guint position) -{ - SysprofProcessModel *self = (SysprofProcessModel *)model; - - g_return_val_if_fail (SYSPROF_IS_PROCESS_MODEL (self), NULL); - g_return_val_if_fail (position < self->items->len, NULL); - - return g_object_ref (g_ptr_array_index (self->items, position)); -} - -static void -list_model_iface_init (GListModelInterface *iface) -{ - iface->get_item_type = sysprof_process_model_get_item_type; - iface->get_n_items = sysprof_process_model_get_n_items; - iface->get_item = sysprof_process_model_get_item; -} - -void -sysprof_process_model_set_no_proxy (SysprofProcessModel *self, - gboolean no_proxy) -{ - g_return_if_fail (SYSPROF_IS_PROCESS_MODEL (self)); - - self->no_proxy = !!no_proxy; -} diff --git a/src/libsysprof/sysprof-process-model.h b/src/libsysprof/sysprof-process-model.h deleted file mode 100644 index 9737b774..00000000 --- a/src/libsysprof/sysprof-process-model.h +++ /dev/null @@ -1,48 +0,0 @@ -/* sysprof-process-model.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include - -#include "sysprof-version-macros.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_PROCESS_MODEL (sysprof_process_model_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofProcessModel, sysprof_process_model, SYSPROF, PROCESS_MODEL, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofProcessModel *sysprof_process_model_new (void); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_process_model_set_no_proxy (SysprofProcessModel *self, - gboolean no_proxy); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_process_model_reload (SysprofProcessModel *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_process_model_queue_reload (SysprofProcessModel *self); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-profile.c b/src/libsysprof/sysprof-profile.c deleted file mode 100644 index de1d2203..00000000 --- a/src/libsysprof/sysprof-profile.c +++ /dev/null @@ -1,85 +0,0 @@ -/* sysprof-profile.c - * - * Copyright 2016-2019 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 - */ - -#include "config.h" - -#include "sysprof-profile.h" - -G_DEFINE_INTERFACE (SysprofProfile, sysprof_profile, G_TYPE_OBJECT) - -static void -dummy_generate (SysprofProfile *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GTask) task = NULL; - - task = g_task_new (self, cancellable, callback, user_data); - g_task_return_boolean (task, TRUE); -} - -static gboolean -dummy_generate_finish (SysprofProfile *self, - GAsyncResult *result, - GError **error) -{ - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -sysprof_profile_default_init (SysprofProfileInterface *iface) -{ - iface->generate = dummy_generate; - iface->generate_finish = dummy_generate_finish; -} - -void -sysprof_profile_generate (SysprofProfile *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_return_if_fail (SYSPROF_IS_PROFILE (self)); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - SYSPROF_PROFILE_GET_IFACE (self)->generate (self, cancellable, callback, user_data); -} - -gboolean -sysprof_profile_generate_finish (SysprofProfile *self, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (SYSPROF_IS_PROFILE (self), FALSE); - g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); - - return SYSPROF_PROFILE_GET_IFACE (self)->generate_finish (self, result, error); -} - -void -sysprof_profile_set_reader (SysprofProfile *self, - SysprofCaptureReader *reader) -{ - g_return_if_fail (SYSPROF_IS_PROFILE (self)); - g_return_if_fail (reader != NULL); - - SYSPROF_PROFILE_GET_IFACE (self)->set_reader (self, reader); -} diff --git a/src/libsysprof/sysprof-profile.h b/src/libsysprof/sysprof-profile.h deleted file mode 100644 index 34b1006a..00000000 --- a/src/libsysprof/sysprof-profile.h +++ /dev/null @@ -1,67 +0,0 @@ -/* sysprof-profile.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include - -#include "sysprof-capture-reader.h" -#include "sysprof-version-macros.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_PROFILE (sysprof_profile_get_type ()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_INTERFACE (SysprofProfile, sysprof_profile, SYSPROF, PROFILE, GObject) - -struct _SysprofProfileInterface -{ - GTypeInterface parent; - - void (*set_reader) (SysprofProfile *self, - SysprofCaptureReader *reader); - void (*generate) (SysprofProfile *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - gboolean (*generate_finish) (SysprofProfile *self, - GAsyncResult *result, - GError **error); -}; - -SYSPROF_AVAILABLE_IN_ALL -void sysprof_profile_set_reader (SysprofProfile *self, - SysprofCaptureReader *reader); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_profile_generate (SysprofProfile *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_profile_generate_finish (SysprofProfile *self, - GAsyncResult *result, - GError **error); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-profiler.c b/src/libsysprof/sysprof-profiler.c deleted file mode 100644 index 12fe8b5b..00000000 --- a/src/libsysprof/sysprof-profiler.c +++ /dev/null @@ -1,312 +0,0 @@ -/* sysprof-profiler.c - * - * Copyright 2016-2019 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 G_LOG_DOMAIN "sysprof-profiler" - -#include "config.h" - -#include "sysprof-profiler.h" - -G_DEFINE_INTERFACE (SysprofProfiler, sysprof_profiler, G_TYPE_OBJECT) - -enum { - FAILED, - STOPPED, - N_SIGNALS -}; - -static guint signals [N_SIGNALS]; - -static void -sysprof_profiler_default_init (SysprofProfilerInterface *iface) -{ - signals [FAILED] = g_signal_new ("failed", - G_TYPE_FROM_INTERFACE (iface), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (SysprofProfilerInterface, failed), - NULL, NULL, NULL, - G_TYPE_NONE, 1, G_TYPE_ERROR); - - signals [STOPPED] = g_signal_new ("stopped", - G_TYPE_FROM_INTERFACE (iface), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (SysprofProfilerInterface, stopped), - NULL, NULL, NULL, - G_TYPE_NONE, 0); - - g_object_interface_install_property (iface, - g_param_spec_double ("elapsed", - "Elapsed", - "The amount of elapsed time profiling", - 0, - G_MAXDOUBLE, - 0, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); - - g_object_interface_install_property (iface, - g_param_spec_boolean ("is-running", - "Is Running", - "If the profiler is currently running.", - FALSE, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); - - g_object_interface_install_property (iface, - g_param_spec_boolean ("is-mutable", - "Is Mutable", - "If the profiler can still be prepared.", - TRUE, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); - - g_object_interface_install_property (iface, - g_param_spec_boolean ("spawn-inherit-environ", - "Sysprofawn Inherit Environ", - "If the spawned child should inherit the parents environment", - TRUE, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - - g_object_interface_install_property (iface, - g_param_spec_boolean ("whole-system", - "Whole System", - "If the whole system should be profiled", - TRUE, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - - g_object_interface_install_property (iface, - g_param_spec_boolean ("spawn", - "Sysprofawn", - "If configured child should be spawned", - TRUE, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - - g_object_interface_install_property (iface, - g_param_spec_boxed ("spawn-argv", - "Sysprofawn Argv", - "The arguments for the spawn child", - G_TYPE_STRV, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - - g_object_interface_install_property (iface, - g_param_spec_string ("spawn-cwd", - "Spawn Working Directory", - "The directory to spawn the application from", - NULL, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - - g_object_interface_install_property (iface, - g_param_spec_boxed ("spawn-env", - "Sysprofawn Environment", - "The environment for the spawn child", - G_TYPE_STRV, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); -} - -gdouble -sysprof_profiler_get_elapsed (SysprofProfiler *self) -{ - gdouble value = 0.0; - g_return_val_if_fail (SYSPROF_IS_PROFILER (self), 0.0); - g_object_get (self, "elapsed", &value, NULL); - return value; -} - -gboolean -sysprof_profiler_get_is_running (SysprofProfiler *self) -{ - gboolean is_running = FALSE; - g_return_val_if_fail (SYSPROF_IS_PROFILER (self), FALSE); - g_object_get (self, "is-running", &is_running, NULL); - return is_running; -} - -gboolean -sysprof_profiler_get_is_mutable (SysprofProfiler *self) -{ - gboolean is_mutable = FALSE; - g_return_val_if_fail (SYSPROF_IS_PROFILER (self), FALSE); - g_object_get (self, "is-mutable", &is_mutable, NULL); - return is_mutable; -} - -gboolean -sysprof_profiler_get_spawn_inherit_environ (SysprofProfiler *self) -{ - gboolean spawn_inherit_environ = FALSE; - g_return_val_if_fail (SYSPROF_IS_PROFILER (self), FALSE); - g_object_get (self, "spawn-inherit-environ", &spawn_inherit_environ, NULL); - return spawn_inherit_environ; -} - -void -sysprof_profiler_set_spawn_inherit_environ (SysprofProfiler *self, - gboolean spawn_inherit_environ) -{ - g_return_if_fail (SYSPROF_IS_PROFILER (self)); - g_object_set (self, "spawn-inherit-environ", !!spawn_inherit_environ, NULL); -} - -gboolean -sysprof_profiler_get_spawn (SysprofProfiler *self) -{ - gboolean spawn = FALSE; - g_return_val_if_fail (SYSPROF_IS_PROFILER (self), FALSE); - g_object_get (self, "spawn", &spawn, NULL); - return spawn; -} - -void -sysprof_profiler_set_spawn_cwd (SysprofProfiler *self, - const gchar *spawn_cwd) -{ - g_return_if_fail (SYSPROF_IS_PROFILER (self)); - g_object_set (self, "spawn-cwd", spawn_cwd, NULL); -} - -void -sysprof_profiler_set_spawn (SysprofProfiler *self, - gboolean spawn) -{ - g_return_if_fail (SYSPROF_IS_PROFILER (self)); - g_object_set (self, "spawn", !!spawn, NULL); -} - -gboolean -sysprof_profiler_get_whole_system (SysprofProfiler *self) -{ - gboolean whole_system = FALSE; - g_return_val_if_fail (SYSPROF_IS_PROFILER (self), FALSE); - g_object_get (self, "whole-system", &whole_system, NULL); - return whole_system; -} - -void -sysprof_profiler_set_whole_system (SysprofProfiler *self, - gboolean whole_system) -{ - g_return_if_fail (SYSPROF_IS_PROFILER (self)); - g_object_set (self, "whole-system", !!whole_system, NULL); -} - -void -sysprof_profiler_set_spawn_argv (SysprofProfiler *self, - const gchar * const *spawn_argv) -{ - g_return_if_fail (SYSPROF_IS_PROFILER (self)); - g_object_set (self, "spawn-argv", spawn_argv, NULL); -} - -void -sysprof_profiler_set_spawn_env (SysprofProfiler *self, - const gchar * const *spawn_env) -{ - g_return_if_fail (SYSPROF_IS_PROFILER (self)); - g_object_set (self, "spawn-env", spawn_env, NULL); -} - -void -sysprof_profiler_add_source (SysprofProfiler *self, - SysprofSource *source) -{ - g_return_if_fail (SYSPROF_IS_PROFILER (self)); - g_return_if_fail (SYSPROF_IS_SOURCE (source)); - - SYSPROF_PROFILER_GET_IFACE (self)->add_source (self, source); -} - -void -sysprof_profiler_set_writer (SysprofProfiler *self, - SysprofCaptureWriter *writer) -{ - g_return_if_fail (SYSPROF_IS_PROFILER (self)); - g_return_if_fail (writer != NULL); - - SYSPROF_PROFILER_GET_IFACE (self)->set_writer (self, writer); -} - -SysprofCaptureWriter * -sysprof_profiler_get_writer (SysprofProfiler *self) -{ - g_return_val_if_fail (SYSPROF_IS_PROFILER (self), NULL); - - return SYSPROF_PROFILER_GET_IFACE (self)->get_writer (self); -} - -void -sysprof_profiler_start (SysprofProfiler *self) -{ - g_return_if_fail (SYSPROF_IS_PROFILER (self)); - - SYSPROF_PROFILER_GET_IFACE (self)->start (self); -} - -void -sysprof_profiler_stop (SysprofProfiler *self) -{ - g_return_if_fail (SYSPROF_IS_PROFILER (self)); - - SYSPROF_PROFILER_GET_IFACE (self)->stop (self); -} - -void -sysprof_profiler_add_pid (SysprofProfiler *self, - GPid pid) -{ - g_return_if_fail (SYSPROF_IS_PROFILER (self)); - g_return_if_fail (pid > -1); - - SYSPROF_PROFILER_GET_IFACE (self)->add_pid (self, pid); -} - -void -sysprof_profiler_remove_pid (SysprofProfiler *self, - GPid pid) -{ - g_return_if_fail (SYSPROF_IS_PROFILER (self)); - g_return_if_fail (pid > -1); - - SYSPROF_PROFILER_GET_IFACE (self)->remove_pid (self, pid); -} - -const GPid * -sysprof_profiler_get_pids (SysprofProfiler *self, - guint *n_pids) -{ - g_return_val_if_fail (SYSPROF_IS_PROFILER (self), NULL); - g_return_val_if_fail (n_pids != NULL, NULL); - - return SYSPROF_PROFILER_GET_IFACE (self)->get_pids (self, n_pids); -} - -void -sysprof_profiler_emit_failed (SysprofProfiler *self, - const GError *error) -{ - g_return_if_fail (SYSPROF_IS_PROFILER (self)); - g_return_if_fail (error != NULL); - - g_signal_emit (self, signals [FAILED], 0, error); -} - -void -sysprof_profiler_emit_stopped (SysprofProfiler *self) -{ - g_return_if_fail (SYSPROF_IS_PROFILER (self)); - - g_signal_emit (self, signals [STOPPED], 0); -} diff --git a/src/libsysprof/sysprof-profiler.h b/src/libsysprof/sysprof-profiler.h deleted file mode 100644 index 24735cdf..00000000 --- a/src/libsysprof/sysprof-profiler.h +++ /dev/null @@ -1,191 +0,0 @@ -/* sysprof-profiler.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include "sysprof-version-macros.h" - -#include "sysprof-capture-writer.h" -#include "sysprof-source.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_PROFILER (sysprof_profiler_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_INTERFACE (SysprofProfiler, sysprof_profiler, SYSPROF, PROFILER, GObject) - -struct _SysprofProfilerInterface -{ - GTypeInterface parent_interface; - - /** - * SysprofProfiler::failed: - * @self: A #SysprofProfiler - * @reason: A #GError representing the reason for the failure - * - * This signal is emitted if the profiler failed. Note that - * #SysprofProfiler::stopped will also be emitted, but does not allow for - * receiving the error condition. - */ - void (*failed) (SysprofProfiler *self, - const GError *error); - - /** - * SysprofProfiler::stopped: - * @self: A #SysprofProfiler - * - * This signal is emitted when a profiler is stopped. It will always be - * emitted after a sysprof_profiler_start() has been called, either after - * completion of sysprof_profiler_stop() or after a failure or after asynchronous - * completion of stopping. - */ - void (*stopped) (SysprofProfiler *self); - - /** - * SysprofProfiler::add_source: - * - * Adds a source to the profiler. - */ - void (*add_source) (SysprofProfiler *profiler, - SysprofSource *source); - - /** - * SysprofProfiler::set_writer: - * - * Sets the writer to use for the profiler. - */ - void (*set_writer) (SysprofProfiler *self, - SysprofCaptureWriter *writer); - - /** - * SysprofProfiler::get_writer: - * - * Gets the writer that is being used to capture. - * - * Returns: (nullable) (transfer none): A #SysprofCaptureWriter. - */ - SysprofCaptureWriter *(*get_writer) (SysprofProfiler *self); - - /** - * SysprofProfiler::start: - * - * Starts the profiler. - */ - void (*start) (SysprofProfiler *self); - - /** - * SysprofProfiler::stop: - * - * Stops the profiler. - */ - void (*stop) (SysprofProfiler *self); - - /** - * SysprofProfiler::add_pid: - * - * Add a pid to be profiled. - */ - void (*add_pid) (SysprofProfiler *self, - GPid pid); - - /** - * SysprofProfiler::remove_pid: - * - * Remove a pid from the profiler. This will not be called after - * SysprofProfiler::start has been called. - */ - void (*remove_pid) (SysprofProfiler *self, - GPid pid); - - /** - * SysprofProfiler::get_pids: - * - * Gets the pids that are part of this profiling session. If no pids - * have been specified, %NULL is returned. - * - * Returns: (nullable) (transfer none): An array of #GPid, or %NULL. - */ - const GPid *(*get_pids) (SysprofProfiler *self, - guint *n_pids); -}; - -SYSPROF_AVAILABLE_IN_ALL -void sysprof_profiler_emit_failed (SysprofProfiler *self, - const GError *error); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_profiler_emit_stopped (SysprofProfiler *self); -SYSPROF_AVAILABLE_IN_ALL -gdouble sysprof_profiler_get_elapsed (SysprofProfiler *self); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_profiler_get_is_mutable (SysprofProfiler *self); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_profiler_get_spawn_inherit_environ (SysprofProfiler *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_profiler_set_spawn_inherit_environ (SysprofProfiler *self, - gboolean spawn_inherit_environ); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_profiler_get_whole_system (SysprofProfiler *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_profiler_set_whole_system (SysprofProfiler *self, - gboolean whole_system); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_profiler_get_spawn (SysprofProfiler *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_profiler_set_spawn (SysprofProfiler *self, - gboolean spawn); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_profiler_set_spawn_argv (SysprofProfiler *self, - const gchar * const *spawn_argv); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_profiler_set_spawn_env (SysprofProfiler *self, - const gchar * const *spawn_env); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_profiler_set_spawn_cwd (SysprofProfiler *self, - const gchar *cwd); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_profiler_add_source (SysprofProfiler *self, - SysprofSource *source); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_profiler_set_writer (SysprofProfiler *self, - SysprofCaptureWriter *writer); -SYSPROF_AVAILABLE_IN_ALL -SysprofCaptureWriter *sysprof_profiler_get_writer (SysprofProfiler *self); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_profiler_get_is_running (SysprofProfiler *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_profiler_start (SysprofProfiler *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_profiler_stop (SysprofProfiler *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_profiler_add_pid (SysprofProfiler *self, - GPid pid); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_profiler_remove_pid (SysprofProfiler *self, - GPid pid); -SYSPROF_AVAILABLE_IN_ALL -const GPid *sysprof_profiler_get_pids (SysprofProfiler *self, - guint *n_pids); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-proxy-source.c b/src/libsysprof/sysprof-proxy-source.c deleted file mode 100644 index f686a19b..00000000 --- a/src/libsysprof/sysprof-proxy-source.c +++ /dev/null @@ -1,753 +0,0 @@ -/* sysprof-proxy-source.c - * - * Copyright 2019 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 G_LOG_DOMAIN "sysprof-proxy-source" - -#include "config.h" - -#include -#include -#include - -#include "sysprof-platform.h" - -#include "sysprof-proxy-source.h" - -struct _SysprofProxySource -{ - GObject parent_instance; - GCancellable *cancellable; - SysprofCaptureWriter *writer; - gchar *bus_name; - gchar *object_path; - GArray *pids; - GPtrArray *monitors; - GBusType bus_type; - guint stopping_count; - guint is_ready : 1; - guint has_started : 1; - guint is_whole_system : 1; -}; - -typedef struct -{ - SysprofProxySource *self; - gchar *name; -} Peer; - -typedef struct -{ - SysprofProxySource *self; - GDBusConnection *bus; - gchar *name; - gchar *object_path; - gint fd; - guint needs_stop : 1; -} Monitor; - -enum { - PROP_0, - PROP_BUS_NAME, - PROP_BUS_TYPE, - PROP_OBJECT_PATH, - N_PROPS -}; - -static GParamSpec *properties[N_PROPS]; - -static inline gint -steal_fd (gint *fd) -{ - gint r = *fd; - *fd = -1; - return r; -} - -static void -_g_weak_ref_free (GWeakRef *wr) -{ - g_weak_ref_clear (wr); - g_slice_free (GWeakRef, wr); -} - -static void -peer_free (Peer *peer) -{ - g_assert (peer != NULL); - - g_clear_object (&peer->self); - g_clear_pointer (&peer->name, g_free); - g_slice_free (Peer, peer); -} - -static void -monitor_free (Monitor *monitor) -{ - if (monitor == NULL) - return; - - if (monitor->needs_stop) - g_dbus_connection_call (monitor->bus, - monitor->name, - monitor->object_path, - "org.gnome.Sysprof3.Profiler", - "Stop", - g_variant_new ("()"), - G_VARIANT_TYPE ("()"), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, - NULL, NULL, NULL); - - if (monitor->fd != -1) - { - close (monitor->fd); - monitor->fd = -1; - } - - g_clear_object (&monitor->self); - g_clear_object (&monitor->bus); - g_clear_pointer (&monitor->name, g_free); - g_clear_pointer (&monitor->object_path, g_free); - g_slice_free (Monitor, monitor); -} - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (Peer, peer_free); -G_DEFINE_AUTOPTR_CLEANUP_FUNC (Monitor, monitor_free); - -static void -sysprof_proxy_source_prepare (SysprofSource *source) -{ - g_assert (SYSPROF_IS_PROXY_SOURCE (source)); - - sysprof_source_emit_ready (source); -} - -static gboolean -sysprof_proxy_source_get_is_ready (SysprofSource *source) -{ - g_assert (SYSPROF_IS_PROXY_SOURCE (source)); - - return TRUE; -} - -static void -sysprof_proxy_source_set_writer (SysprofSource *source, - SysprofCaptureWriter *writer) -{ - SysprofProxySource *self = (SysprofProxySource *)source; - - g_assert (SYSPROF_IS_PROXY_SOURCE (self)); - g_assert (writer != NULL); - - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); - self->writer = sysprof_capture_writer_ref (writer); -} - -static void -sysprof_proxy_source_take_monitor (SysprofProxySource *self, - Monitor *monitor) -{ - g_assert (SYSPROF_IS_PROXY_SOURCE (self)); - g_assert (monitor != NULL); - g_assert (monitor->self == self); - g_assert (monitor->bus == NULL || G_IS_DBUS_CONNECTION (monitor->bus)); - - if (g_cancellable_is_cancelled (self->cancellable)) - monitor_free (monitor); - else - g_ptr_array_add (self->monitors, g_steal_pointer (&monitor)); -} - -static void -sysprof_proxy_source_start_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GDBusConnection *bus = (GDBusConnection *)object; - g_autoptr(Monitor) monitor = user_data; - g_autoptr(GVariant) reply = NULL; - g_autoptr(GError) error = NULL; - SysprofProxySource *self; - - g_assert (G_IS_DBUS_CONNECTION (bus)); - g_assert (monitor != NULL); - g_assert (SYSPROF_IS_PROXY_SOURCE (monitor->self)); - g_assert (G_IS_ASYNC_RESULT (result)); - - if (!(reply = g_dbus_connection_call_with_unix_fd_list_finish (bus, NULL, result, &error))) - { - g_dbus_error_strip_remote_error (error); - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - monitor->needs_stop = TRUE; - g_message ("Failure or no profiler available on peer %s: %s", - monitor->name, error->message); - return; - } - - self = monitor->self; - monitor->needs_stop = TRUE; - sysprof_proxy_source_take_monitor (self, g_steal_pointer (&monitor)); -} - -static void -sysprof_proxy_source_monitor (SysprofProxySource *self, - GDBusConnection *bus, - const gchar *bus_name) -{ - g_autoptr(GUnixFDList) fd_list = NULL; - g_autoptr(GError) error = NULL; - Monitor *monitor; - gint fd; - gint handle; - - g_assert (SYSPROF_IS_PROXY_SOURCE (self)); - g_assert (G_IS_DBUS_CONNECTION (bus)); - g_assert (bus_name != NULL); - - if (g_cancellable_is_cancelled (self->cancellable)) - return; - - fd_list = g_unix_fd_list_new (); - - if (-1 == (fd = sysprof_memfd_create ("[sysprof-proxy-capture]")) || - -1 == (handle = g_unix_fd_list_append (fd_list, fd, &error))) - { - if (fd != -1) - close (fd); - g_warning ("Failed to create memfd for peer: %s", error->message); - return; - } - - monitor = g_slice_new0 (Monitor); - monitor->self = g_object_ref (self); - monitor->bus = g_object_ref (bus); - monitor->name = g_strdup (bus_name); - monitor->object_path = g_strdup (self->object_path); - monitor->fd = fd; - - g_dbus_connection_call_with_unix_fd_list (bus, - bus_name, - self->object_path, - "org.gnome.Sysprof3.Profiler", - "Start", - g_variant_new ("(a{sv}h)", NULL, handle), - G_VARIANT_TYPE ("()"), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, - fd_list, - self->cancellable, - sysprof_proxy_source_start_cb, - g_steal_pointer (&monitor)); -} - -static void -sysprof_proxy_source_get_pid_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GDBusConnection *bus = (GDBusConnection *)object; - g_autoptr(Peer) peer = user_data; - g_autoptr(GVariant) reply = NULL; - g_autoptr(GError) error = NULL; - guint32 pid = 0; - - g_assert (G_IS_DBUS_CONNECTION (bus)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (peer != NULL); - g_assert (SYSPROF_IS_PROXY_SOURCE (peer->self)); - - if (!(reply = g_dbus_connection_call_finish (bus, result, &error))) - return; - - g_variant_get (reply, "(u)", &pid); - - /* If we don't care about this PID, then ignore it */ - for (guint i = 0; i < peer->self->pids->len; i++) - { - if ((GPid)pid == g_array_index (peer->self->pids, GPid, i)) - { - sysprof_proxy_source_monitor (peer->self, bus, peer->name); - return; - } - } -} - -static void -sysprof_proxy_source_list_names_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GDBusConnection *bus = (GDBusConnection *)object; - g_autofree const gchar **names = NULL; - g_autoptr(SysprofProxySource) self = user_data; - g_autoptr(GVariant) reply = NULL; - g_autoptr(GError) error = NULL; - - g_assert (G_IS_DBUS_CONNECTION (bus)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (SYSPROF_IS_PROXY_SOURCE (self)); - - if (!(reply = g_dbus_connection_call_finish (bus, result, &error))) - { - g_warning ("Failed to list D-Bus peer names: %s", error->message); - return; - } - - g_variant_get (reply, "(^a&s)", &names); - - for (guint i = 0; names[i] != NULL; i++) - { - Peer *peer; - - peer = g_slice_new (Peer); - peer->self = g_object_ref (self); - peer->name = g_strdup (names[i]); - - g_dbus_connection_call (bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "GetConnectionUnixProcessID", - g_variant_new ("(s)", names[i]), - G_VARIANT_TYPE ("(u)"), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, - self->cancellable, - sysprof_proxy_source_get_pid_cb, - g_steal_pointer (&peer)); - } -} - -static void -sysprof_proxy_source_name_owner_changed_cb (GDBusConnection *connection, - const gchar *sender_name, - const gchar *object_path, - const gchar *interface_name, - const gchar *signal_name, - GVariant *params, - gpointer user_data) -{ - GWeakRef *wr = user_data; - g_autoptr(SysprofProxySource) self = NULL; - const gchar *name; - const gchar *old_name; - const gchar *new_name; - - g_assert (G_IS_DBUS_CONNECTION (connection)); - g_assert (params != NULL); - g_assert (g_variant_is_of_type (params, G_VARIANT_TYPE ("(sss)"))); - g_assert (wr != NULL); - - g_variant_get (params, "(&s&s&s)", &name, &old_name, &new_name); - - if (!(self = g_weak_ref_get (wr))) - return; - - if (self->bus_name != NULL && g_strcmp0 (name, self->bus_name) == 0) - sysprof_proxy_source_monitor (self, connection, new_name); -} - -static void -sysprof_proxy_source_get_bus_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - g_autoptr(SysprofProxySource) self = user_data; - g_autoptr(GDBusConnection) bus = NULL; - g_autoptr(GError) error = NULL; - - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (SYSPROF_IS_PROXY_SOURCE (self)); - - if (!(bus = g_bus_get_finish (result, &error))) - { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - sysprof_source_emit_failed (SYSPROF_SOURCE (self), error); - return; - } - - if (self->bus_name != NULL && g_dbus_is_name (self->bus_name)) - { - GWeakRef *wr; - - /* Try to monitor immediately */ - sysprof_proxy_source_monitor (self, bus, self->bus_name); - - /* Watch for changes in case the program isn't started yet and - * we want to monitor it after it is available. - */ - wr = g_slice_new0 (GWeakRef); - g_weak_ref_init (wr, self); - g_dbus_connection_signal_subscribe (bus, - NULL, - "org.freedesktop.DBus", - "NameOwnerChanged", - NULL, - NULL, - 0, - sysprof_proxy_source_name_owner_changed_cb, - g_steal_pointer (&wr), - (GDestroyNotify)_g_weak_ref_free); - } - - if (self->pids->len > 0) - { - /* We need to query the processes that have been spawned to see - * if they have our proxy address associated with them. But first, - * we need to find what pids own what connection. - */ - g_dbus_connection_call (bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "ListNames", - g_variant_new ("()"), - G_VARIANT_TYPE ("(as)"), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, - self->cancellable, - sysprof_proxy_source_list_names_cb, - g_object_ref (self)); - return; - } -} - -static void -sysprof_proxy_source_start (SysprofSource *source) -{ - SysprofProxySource *self = (SysprofProxySource *)source; - - g_assert (SYSPROF_IS_PROXY_SOURCE (self)); - - self->has_started = TRUE; - - g_bus_get (self->bus_type, - self->cancellable, - sysprof_proxy_source_get_bus_cb, - g_object_ref (self)); -} - -static void -sysprof_proxy_source_cat (SysprofProxySource *self, - SysprofCaptureReader *reader) -{ - g_assert (SYSPROF_IS_PROXY_SOURCE (self)); - g_assert (reader != NULL); - - if (self->writer != NULL && - !sysprof_capture_writer_cat (self->writer, reader)) - { - int errsv = errno; - g_warning ("Failed to join reader: %s", g_strerror (errsv)); - } -} - -static void -sysprof_proxy_source_complete_monitor (SysprofProxySource *self, - Monitor *monitor) -{ - g_autoptr(SysprofCaptureReader) reader = NULL; - - g_assert (SYSPROF_IS_PROXY_SOURCE (self)); - g_assert (monitor != NULL); - g_assert (monitor->self == self); - - if (!(reader = sysprof_capture_reader_new_from_fd (steal_fd (&monitor->fd)))) - { - int errsv = errno; - g_warning ("Failed to load reader from peer FD: %s", g_strerror (errsv)); - } - else - sysprof_proxy_source_cat (self, reader); -} - -static void -sysprof_proxy_source_stop_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - GDBusConnection *bus = (GDBusConnection *)object; - g_autoptr(Monitor) monitor = user_data; - g_autoptr(GVariant) reply = NULL; - g_autoptr(GError) error = NULL; - SysprofProxySource *self; - - g_assert (G_IS_DBUS_CONNECTION (bus)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (monitor != NULL); - - self = monitor->self; - reply = g_dbus_connection_call_finish (bus, result, &error); - monitor->needs_stop = FALSE; - - sysprof_proxy_source_complete_monitor (self, monitor); - - self->stopping_count--; - - if (self->stopping_count == 0) - sysprof_source_emit_finished (SYSPROF_SOURCE (self)); -} - -static void -sysprof_proxy_source_stop (SysprofSource *source) -{ - SysprofProxySource *self = (SysprofProxySource *)source; - - g_assert (SYSPROF_IS_PROXY_SOURCE (self)); - - g_cancellable_cancel (self->cancellable); - - for (guint i = 0; i < self->monitors->len; i++) - { - g_autoptr(Monitor) monitor = g_ptr_array_index (self->monitors, i); - - g_ptr_array_index (self->monitors, i) = NULL; - - if (monitor->needs_stop) - { - self->stopping_count++; - g_dbus_connection_call (monitor->bus, - monitor->name, - monitor->object_path, - "org.gnome.Sysprof3.Profiler", - "Stop", - g_variant_new ("()"), - G_VARIANT_TYPE ("()"), - G_DBUS_CALL_FLAGS_NO_AUTO_START, - -1, - NULL, - sysprof_proxy_source_stop_cb, - monitor); - monitor = NULL; /* stolen */ - } - else - { - sysprof_proxy_source_complete_monitor (self, monitor); - } - } - - if (self->stopping_count == 0) - sysprof_source_emit_finished (source); -} - -static void -sysprof_proxy_source_add_pid (SysprofSource *source, - GPid pid) -{ - SysprofProxySource *self = (SysprofProxySource *)source; - - g_assert (SYSPROF_IS_PROXY_SOURCE (self)); - g_assert (pid > 0); - - if (!self->has_started) - self->is_whole_system = FALSE; - - g_array_append_val (self->pids, pid); -} - -static void -sysprof_proxy_source_serialize (SysprofSource *source, - GKeyFile *keyfile, - const gchar *group) -{ - SysprofProxySource *self = (SysprofProxySource *)source; - - g_assert (SYSPROF_IS_PROXY_SOURCE (self)); - g_assert (keyfile != NULL); - g_assert (group != NULL); - - g_key_file_set_string (keyfile, group, "bus-name", self->bus_name ?: ""); - g_key_file_set_string (keyfile, group, "object-path", self->object_path ?: ""); - g_key_file_set_integer (keyfile, group, "bus-type", self->bus_type); -} - -static void -sysprof_proxy_source_deserialize (SysprofSource *source, - GKeyFile *keyfile, - const gchar *group) -{ - SysprofProxySource *self = (SysprofProxySource *)source; - gint bus_type; - - g_assert (SYSPROF_IS_PROXY_SOURCE (self)); - g_assert (keyfile != NULL); - g_assert (group != NULL); - - g_clear_pointer (&self->bus_name, g_free); - g_clear_pointer (&self->object_path, g_free); - - self->bus_name = g_key_file_get_string (keyfile, group, "bus-name", NULL); - self->object_path = g_key_file_get_string (keyfile, group, "object-path", NULL); - - bus_type = g_key_file_get_integer (keyfile, group, "bus-type", NULL); - if (bus_type == G_BUS_TYPE_SESSION || bus_type == G_BUS_TYPE_SYSTEM) - self->bus_type = bus_type; -} - -static void -source_iface_init (SysprofSourceInterface *iface) -{ - iface->add_pid = sysprof_proxy_source_add_pid; - iface->prepare = sysprof_proxy_source_prepare; - iface->set_writer = sysprof_proxy_source_set_writer; - iface->get_is_ready = sysprof_proxy_source_get_is_ready; - iface->stop = sysprof_proxy_source_stop; - iface->start = sysprof_proxy_source_start; - iface->serialize = sysprof_proxy_source_serialize; - iface->deserialize = sysprof_proxy_source_deserialize; -} - -G_DEFINE_TYPE_WITH_CODE (SysprofProxySource, sysprof_proxy_source, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init)) - -static void -sysprof_proxy_source_finalize (GObject *object) -{ - SysprofProxySource *self = (SysprofProxySource *)object; - - g_clear_pointer (&self->monitors, g_ptr_array_unref); - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); - g_clear_pointer (&self->bus_name, g_free); - g_clear_pointer (&self->object_path, g_free); - g_clear_pointer (&self->pids, g_array_unref); - g_clear_object (&self->cancellable); - - G_OBJECT_CLASS (sysprof_proxy_source_parent_class)->finalize (object); -} - -static void -sysprof_proxy_source_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofProxySource *self = SYSPROF_PROXY_SOURCE (object); - - switch (prop_id) - { - case PROP_BUS_TYPE: - g_value_set_enum (value, self->bus_type); - break; - - case PROP_BUS_NAME: - g_value_set_string (value, self->bus_name); - break; - - case PROP_OBJECT_PATH: - g_value_set_string (value, self->object_path); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_proxy_source_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofProxySource *self = SYSPROF_PROXY_SOURCE (object); - - switch (prop_id) - { - case PROP_BUS_TYPE: - self->bus_type = g_value_get_enum (value); - break; - - case PROP_BUS_NAME: - g_free (self->bus_name); - self->bus_name = g_value_dup_string (value); - break; - - case PROP_OBJECT_PATH: - g_free (self->object_path); - self->object_path = g_value_dup_string (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_proxy_source_class_init (SysprofProxySourceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_proxy_source_finalize; - object_class->get_property = sysprof_proxy_source_get_property; - object_class->set_property = sysprof_proxy_source_set_property; - - properties [PROP_BUS_TYPE] = - g_param_spec_enum ("bus-type", NULL, NULL, - G_TYPE_BUS_TYPE, - G_BUS_TYPE_SESSION, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_BUS_NAME] = - g_param_spec_string ("bus-name", NULL, NULL, - NULL, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_OBJECT_PATH] = - g_param_spec_string ("object-path", NULL, NULL, - NULL, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_proxy_source_init (SysprofProxySource *self) -{ - self->cancellable = g_cancellable_new (); - self->pids = g_array_new (FALSE, FALSE, sizeof (GPid)); - self->monitors = g_ptr_array_new_with_free_func ((GDestroyNotify) monitor_free); - self->is_whole_system = TRUE; - self->bus_type = G_BUS_TYPE_SESSION; -} - -SysprofSource * -sysprof_proxy_source_new (GBusType bus_type, - const gchar *bus_name, - const gchar *object_path) -{ - SysprofProxySource *self; - - g_return_val_if_fail (bus_type == G_BUS_TYPE_SESSION || bus_type == G_BUS_TYPE_SYSTEM, NULL); - g_return_val_if_fail (bus_name != NULL, NULL); - g_return_val_if_fail (object_path != NULL, NULL); - - if (bus_name && !*bus_name) - bus_name = NULL; - - if (object_path && !*object_path) - object_path = NULL; - - self = g_object_new (SYSPROF_TYPE_PROXY_SOURCE, - "bus-type", bus_type, - "bus-name", bus_name, - "object-path", object_path, - NULL); - - return SYSPROF_SOURCE (g_steal_pointer (&self)); -} diff --git a/src/libsysprof/sysprof-proxy-source.h b/src/libsysprof/sysprof-proxy-source.h deleted file mode 100644 index 284d1bcd..00000000 --- a/src/libsysprof/sysprof-proxy-source.h +++ /dev/null @@ -1,39 +0,0 @@ -/* sysprof-proxy-source.h - * - * Copyright 2019 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 - */ - -#pragma once - -#include - -#include "sysprof-source.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_PROXY_SOURCE (sysprof_proxy_source_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofProxySource, sysprof_proxy_source, SYSPROF, PROXY_SOURCE, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSource *sysprof_proxy_source_new (GBusType bus_type, - const gchar *bus_name, - const gchar *object_path); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-selection.c b/src/libsysprof/sysprof-selection.c deleted file mode 100644 index 521aff53..00000000 --- a/src/libsysprof/sysprof-selection.c +++ /dev/null @@ -1,335 +0,0 @@ -/* sysprof-selection.c - * - * Copyright 2016-2019 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 G_LOG_DOMAIN "sysprof-selection" - -#include "config.h" - -#include "sysprof-selection.h" - -struct _SysprofSelection -{ - GObject parent_instance; - GArray *ranges; -}; - -typedef struct -{ - gint64 begin; - gint64 end; -} Range; - -G_DEFINE_TYPE (SysprofSelection, sysprof_selection, G_TYPE_OBJECT) - -enum { - PROP_0, - PROP_HAS_SELECTION, - N_PROPS -}; - -enum { - CHANGED, - N_SIGNALS -}; - -static GParamSpec *properties [N_PROPS]; -static guint signals [N_SIGNALS]; - -static inline void -int64_swap (gint64 *a, - gint64 *b) -{ - if (*a > *b) - { - gint64 tmp = *a; - *a = *b; - *b = tmp; - } -} - -static void -sysprof_selection_finalize (GObject *object) -{ - SysprofSelection *self = (SysprofSelection *)object; - - g_clear_pointer (&self->ranges, g_array_unref); - - G_OBJECT_CLASS (sysprof_selection_parent_class)->finalize (object); -} - -static void -sysprof_selection_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofSelection *self = SYSPROF_SELECTION (object); - - switch (prop_id) - { - case PROP_HAS_SELECTION: - g_value_set_boolean (value, sysprof_selection_get_has_selection (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_selection_class_init (SysprofSelectionClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_selection_finalize; - object_class->get_property = sysprof_selection_get_property; - - properties [PROP_HAS_SELECTION] = - g_param_spec_boolean ("has-selection", - "Has Selection", - "Has Selection", - FALSE, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - /** - * SysprofSelection::changed: - * - * This signal is emitted when the selection has changed. - */ - signals [CHANGED] = - g_signal_new ("changed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, NULL, G_TYPE_NONE, 0); -} - -static void -sysprof_selection_init (SysprofSelection *self) -{ - self->ranges = g_array_new (FALSE, FALSE, sizeof (Range)); -} - -gboolean -sysprof_selection_get_has_selection (SysprofSelection *self) -{ - g_return_val_if_fail (SYSPROF_IS_SELECTION (self), FALSE); - - return self->ranges->len > 0; -} - -/** - * sysprof_selection_foreach: - * @self: A #SysprofSelection - * @foreach_func: (scope call): a callback for each range - * @user_data: user data for @foreach_func - * - * Calls @foreach_func for every selected range. - */ -void -sysprof_selection_foreach (SysprofSelection *self, - SysprofSelectionForeachFunc foreach_func, - gpointer user_data) -{ - g_return_if_fail (SYSPROF_IS_SELECTION (self)); - g_return_if_fail (foreach_func != NULL); - - for (guint i = 0; i < self->ranges->len; i++) - { - const Range *range = &g_array_index (self->ranges, Range, i); - foreach_func (self, range->begin, range->end, user_data); - } -} - -static gint -range_compare (gconstpointer a, - gconstpointer b) -{ - const Range *ra = a; - const Range *rb = b; - - if (ra->begin < rb->begin) - return -1; - - if (rb->begin < ra->begin) - return 1; - - if (ra->end < rb->end) - return -1; - - if (rb->end < ra->end) - return 1; - - return 0; -} - -static void -join_overlapping (GArray *ranges) -{ - if (ranges->len > 1) - { - guint i = 0; - - while (i < ranges->len - 1) - { - Range *range; - Range next; - - range = &g_array_index (ranges, Range, i); - next = g_array_index (ranges, Range, i + 1); - - if (range->end > next.begin) - { - range->end = next.end; - g_array_remove_index (ranges, i + 1); - } - else - { - i++; - } - } - } -} - -void -sysprof_selection_select_range (SysprofSelection *self, - gint64 begin_time, - gint64 end_time) -{ - Range range = { 0 }; - - g_return_if_fail (SYSPROF_IS_SELECTION (self)); - - int64_swap (&begin_time, &end_time); - - range.begin = begin_time; - range.end = end_time; - - g_array_append_val (self->ranges, range); - g_array_sort (self->ranges, range_compare); - join_overlapping (self->ranges); - - if (self->ranges->len == 1) - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_SELECTION]); - - g_signal_emit (self, signals [CHANGED], 0); -} - -void -sysprof_selection_unselect_range (SysprofSelection *self, - gint64 begin, - gint64 end) -{ - g_return_if_fail (SYSPROF_IS_SELECTION (self)); - - int64_swap (&begin, &end); - - for (guint i = 0; i < self->ranges->len; i++) - { - const Range *range = &g_array_index (self->ranges, Range, i); - - if (range->begin == begin && range->end == end) - { - g_array_remove_index (self->ranges, i); - if (self->ranges->len == 0) - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_SELECTION]); - g_signal_emit (self, signals [CHANGED], 0); - break; - } - } -} - -void -sysprof_selection_unselect_all (SysprofSelection *self) -{ - g_return_if_fail (SYSPROF_IS_SELECTION (self)); - - if (self->ranges->len > 0) - { - g_array_remove_range (self->ranges, 0, self->ranges->len); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_SELECTION]); - g_signal_emit (self, signals [CHANGED], 0); - } -} - -gboolean -sysprof_selection_contains (SysprofSelection *self, - gint64 time_at) -{ - if (self == NULL || self->ranges->len == 0) - return TRUE; - - for (guint i = 0; i < self->ranges->len; i++) - { - const Range *range = &g_array_index (self->ranges, Range, i); - - if (time_at >= range->begin && time_at <= range->end) - return TRUE; - } - - return FALSE; -} - -SysprofSelection * -sysprof_selection_copy (const SysprofSelection *self) -{ - SysprofSelection *copy; - - if (self == NULL) - return NULL; - - copy = g_object_new (SYSPROF_TYPE_SELECTION, NULL); - - for (guint i = 0; i < self->ranges->len; i++) - { - Range range = g_array_index (self->ranges, Range, i); - g_array_append_val (copy->ranges, range); - } - - return copy; -} - -guint -sysprof_selection_get_n_ranges (SysprofSelection *self) -{ - g_return_val_if_fail (SYSPROF_IS_SELECTION (self), 0); - return self->ranges ? self->ranges->len : 0; -} - -void -sysprof_selection_get_nth_range (SysprofSelection *self, - guint nth, - gint64 *begin_time, - gint64 *end_time) -{ - Range r = {0}; - - g_return_if_fail (SYSPROF_IS_SELECTION (self)); - - if (self->ranges && nth < self->ranges->len) - r = g_array_index (self->ranges, Range, nth); - - if (begin_time) - *begin_time = r.begin; - - if (end_time) - *end_time = r.end; -} diff --git a/src/libsysprof/sysprof-selection.h b/src/libsysprof/sysprof-selection.h deleted file mode 100644 index 53141a83..00000000 --- a/src/libsysprof/sysprof-selection.h +++ /dev/null @@ -1,72 +0,0 @@ -/* sysprof-selection.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include - -#include "sysprof-version-macros.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_SELECTION (sysprof_selection_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofSelection, sysprof_selection, SYSPROF, SELECTION, GObject) - -typedef void (*SysprofSelectionForeachFunc) (SysprofSelection *self, - gint64 begin_time, - gint64 end_time, - gpointer user_data); - -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_selection_get_has_selection (SysprofSelection *self); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_selection_contains (SysprofSelection *self, - gint64 time_at); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_selection_select_range (SysprofSelection *self, - gint64 begin_time, - gint64 end_time); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_selection_unselect_range (SysprofSelection *self, - gint64 begin, - gint64 end); -SYSPROF_AVAILABLE_IN_ALL -guint sysprof_selection_get_n_ranges (SysprofSelection *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_selection_get_nth_range (SysprofSelection *self, - guint nth, - gint64 *begin_time, - gint64 *end_time); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_selection_unselect_all (SysprofSelection *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_selection_foreach (SysprofSelection *self, - SysprofSelectionForeachFunc foreach_func, - gpointer user_data); -SYSPROF_AVAILABLE_IN_ALL -SysprofSelection *sysprof_selection_copy (const SysprofSelection *self); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-source.c b/src/libsysprof/sysprof-source.c deleted file mode 100644 index 22a9e13d..00000000 --- a/src/libsysprof/sysprof-source.c +++ /dev/null @@ -1,189 +0,0 @@ -/* sysprof-source.c - * - * Copyright 2016-2019 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 - */ - -#include "config.h" - -#include "sysprof-source.h" - -G_DEFINE_INTERFACE (SysprofSource, sysprof_source, G_TYPE_OBJECT) - -enum { - FAILED, - FINISHED, - READY, - N_SIGNALS -}; - -static guint signals [N_SIGNALS]; - -static void -sysprof_source_default_init (SysprofSourceInterface *iface) -{ - signals [FAILED] = g_signal_new ("failed", - G_TYPE_FROM_INTERFACE (iface), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 1, G_TYPE_ERROR); - - signals [FINISHED] = g_signal_new ("finished", - G_TYPE_FROM_INTERFACE (iface), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, NULL, G_TYPE_NONE, 0); - - signals [READY] = g_signal_new ("ready", - G_TYPE_FROM_INTERFACE (iface), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, NULL, G_TYPE_NONE, 0); -} - -void -sysprof_source_add_pid (SysprofSource *self, - GPid pid) -{ - g_return_if_fail (SYSPROF_IS_SOURCE (self)); - g_return_if_fail (pid != FALSE); - - if (SYSPROF_SOURCE_GET_IFACE (self)->add_pid) - SYSPROF_SOURCE_GET_IFACE (self)->add_pid (self, pid); -} - -void -sysprof_source_emit_finished (SysprofSource *self) -{ - g_return_if_fail (SYSPROF_IS_SOURCE (self)); - - g_signal_emit (self, signals [FINISHED], 0); -} - -void -sysprof_source_emit_failed (SysprofSource *self, - const GError *error) -{ - g_return_if_fail (SYSPROF_IS_SOURCE (self)); - g_return_if_fail (error != NULL); - - g_signal_emit (self, signals [FAILED], 0, error); -} - -void -sysprof_source_emit_ready (SysprofSource *self) -{ - g_return_if_fail (SYSPROF_IS_SOURCE (self)); - - g_signal_emit (self, signals [READY], 0); -} - -gboolean -sysprof_source_get_is_ready (SysprofSource *self) -{ - g_return_val_if_fail (SYSPROF_IS_SOURCE (self), FALSE); - - if (SYSPROF_SOURCE_GET_IFACE (self)->get_is_ready) - return SYSPROF_SOURCE_GET_IFACE (self)->get_is_ready (self); - - return TRUE; -} - -void -sysprof_source_prepare (SysprofSource *self) -{ - g_return_if_fail (SYSPROF_IS_SOURCE (self)); - - if (SYSPROF_SOURCE_GET_IFACE (self)->prepare) - SYSPROF_SOURCE_GET_IFACE (self)->prepare (self); -} - -void -sysprof_source_set_writer (SysprofSource *self, - SysprofCaptureWriter *writer) -{ - g_return_if_fail (SYSPROF_IS_SOURCE (self)); - g_return_if_fail (writer != NULL); - - if (SYSPROF_SOURCE_GET_IFACE (self)->set_writer) - SYSPROF_SOURCE_GET_IFACE (self)->set_writer (self, writer); -} - -void -sysprof_source_start (SysprofSource *self) -{ - g_return_if_fail (SYSPROF_IS_SOURCE (self)); - - if (SYSPROF_SOURCE_GET_IFACE (self)->start) - SYSPROF_SOURCE_GET_IFACE (self)->start (self); -} - -void -sysprof_source_stop (SysprofSource *self) -{ - g_return_if_fail (SYSPROF_IS_SOURCE (self)); - - if (SYSPROF_SOURCE_GET_IFACE (self)->stop) - SYSPROF_SOURCE_GET_IFACE (self)->stop (self); -} - -void -sysprof_source_modify_spawn (SysprofSource *self, - SysprofSpawnable *spawnable) -{ - g_return_if_fail (SYSPROF_IS_SOURCE (self)); - g_return_if_fail (SYSPROF_IS_SPAWNABLE (spawnable)); - - if (SYSPROF_SOURCE_GET_IFACE (self)->modify_spawn) - SYSPROF_SOURCE_GET_IFACE (self)->modify_spawn (self, spawnable); -} - -void -sysprof_source_serialize (SysprofSource *self, - GKeyFile *keyfile, - const gchar *group) -{ - g_return_if_fail (SYSPROF_IS_SOURCE (self)); - g_return_if_fail (keyfile != NULL); - g_return_if_fail (group != NULL); - - if (SYSPROF_SOURCE_GET_IFACE (self)->serialize) - SYSPROF_SOURCE_GET_IFACE (self)->serialize (self, keyfile, group); -} - -void -sysprof_source_deserialize (SysprofSource *self, - GKeyFile *keyfile, - const gchar *group) -{ - g_return_if_fail (SYSPROF_IS_SOURCE (self)); - g_return_if_fail (keyfile != NULL); - g_return_if_fail (group != NULL); - - if (SYSPROF_SOURCE_GET_IFACE (self)->deserialize) - SYSPROF_SOURCE_GET_IFACE (self)->deserialize (self, keyfile, group); -} - -void -sysprof_source_supplement (SysprofSource *self, - SysprofCaptureReader *reader) -{ - g_return_if_fail (SYSPROF_IS_SOURCE (self)); - g_return_if_fail (reader != NULL); - - if (SYSPROF_SOURCE_GET_IFACE (self)->supplement) - SYSPROF_SOURCE_GET_IFACE (self)->supplement (self, reader); -} diff --git a/src/libsysprof/sysprof-source.h b/src/libsysprof/sysprof-source.h deleted file mode 100644 index 412aa765..00000000 --- a/src/libsysprof/sysprof-source.h +++ /dev/null @@ -1,212 +0,0 @@ -/* sysprof-source.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include -#include -#include - -#include "sysprof-spawnable.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_SOURCE (sysprof_source_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_INTERFACE (SysprofSource, sysprof_source, SYSPROF, SOURCE, GObject) - -struct _SysprofSourceInterface -{ - GTypeInterface parent_iface; - - /** - * SysprofSource::get_is_ready: - * @self: A SysprofSource. - * - * This function should return %TRUE if the source is ready to start - * profiling. If the source is not ready until after sysprof_source_start() has - * been called, use sysprof_source_emit_ready() to notify the profiler that the - * source is ready for profiling. - * - * Returns: %TRUE if the source is ready to start profiling. - */ - gboolean (*get_is_ready) (SysprofSource *self); - - /** - * SysprofSource::set_writer: - * @self: A #SysprofSource. - * @writer: A #SysprofCaptureWriter - * - * Sets the #SysprofCaptureWriter to use when profiling. @writer is only safe to - * use from the main thread. If you need to capture from a thread, you should - * create a memory-based #SysprofCaptureWriter and then splice that into this - * writer from the main thread when profiling completes. - * - * See sysprof_capture_writer_splice() for information on splicing writers. - */ - void (*set_writer) (SysprofSource *self, - SysprofCaptureWriter *writer); - - /** - * SysprofSource::prepare: - * - * This function is called before profiling has started. The source should - * prepare any pre-profiling setup here. It may perform this work - * asynchronously, but must g_object_notify() the SysprofSource::is-ready - * property once that asynchronous work has been performed. Until it - * is ready, #SysprofSource::is-ready must return FALSE. - */ - void (*prepare) (SysprofSource *self); - - /** - * SysprofSource::add_pid: - * @self: A #SysprofSource - * @pid: A pid_t > -1 - * - * This function is used to notify the #SysprofSource that a new process, - * identified by @pid, should be profiled. By default, sources should - * assume all processes, and only restrict to a given set of pids if - * this function is called. - */ - void (*add_pid) (SysprofSource *self, - GPid pid); - - /** - * SysprofSource::start: - * @self: A #SysprofSource. - * - * Start profiling as configured. - * - * If a failure occurs while processing, the source should notify the - * profiling session via sysprof_source_emit_failed() from the main thread. - */ - void (*start) (SysprofSource *self); - - /** - * SysprofSource::stop: - * @self: A #SysprofSource. - * - * Stop capturing a profile. The source should immediately stop - * profiling and perform any cleanup tasks required. If doing - * off-main-thread capturing, this is a good time to splice your - * capture into the capture file set with sysprof_source_set_writer(). - * - * If you need to perform asynchronous cleanup, call - * sysprof_source_emit_finished() once that work has completed. If you do - * not need to perform asynchronous cleanup, call - * sysprof_source_emit_finished() from this function. - * - * sysprof_source_emit_finished() must be called from the main-thread. - */ - void (*stop) (SysprofSource *self); - - /** - * SysprofSource::modify-spawn: - * @self: a #SysprofSource - * @spawnable: a #SysprofSpawnable - * - * Allows the source to modify the launcher or argv before the - * process is spawned. - */ - void (*modify_spawn) (SysprofSource *self, - SysprofSpawnable *spawnable); - - /** - * SysprofSource::supplement: - * - * The "supplement" vfunc is called when a source should attempt to add - * any additional data to the trace file based on existing data within - * the trace file. A #SysprofCaptureReader is provided to simplify this - * process for the vfunc. It should write to the writer provided in - * sysprof_source_set_writer(). - * - * This function must finish synchronously. - */ - void (*supplement) (SysprofSource *self, - SysprofCaptureReader *reader); - - /** - * SysprofSource::serialize: - * @self: a #SysprofSource - * @keyfile: a #GKeyFile - * @group: the keyfile group to use - * - * Requests that the source serialize itself into the keyfile - * so that it may be replayed at a future point in time. - */ - void (*serialize) (SysprofSource *self, - GKeyFile *keyfile, - const gchar *group); - - /** - * SysprofSource::deserialize: - * @self: a #SysprofSource - * @keyfile: a #GKeyFile - * @group: the keyfile group to use - * - * Deserialize from the saved state. - */ - void (*deserialize) (SysprofSource *self, - GKeyFile *keyfile, - const gchar *group); -}; - -SYSPROF_AVAILABLE_IN_ALL -void sysprof_source_add_pid (SysprofSource *self, - GPid pid); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_source_emit_ready (SysprofSource *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_source_emit_finished (SysprofSource *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_source_emit_failed (SysprofSource *self, - const GError *error); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_source_get_is_ready (SysprofSource *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_source_prepare (SysprofSource *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_source_set_writer (SysprofSource *self, - SysprofCaptureWriter *writer); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_source_start (SysprofSource *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_source_stop (SysprofSource *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_source_modify_spawn (SysprofSource *self, - SysprofSpawnable *spawnable); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_source_serialize (SysprofSource *self, - GKeyFile *keyfile, - const gchar *group); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_source_deserialize (SysprofSource *self, - GKeyFile *keyfile, - const gchar *group); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_source_supplement (SysprofSource *self, - SysprofCaptureReader *reader); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-spawnable.c b/src/libsysprof/sysprof-spawnable.c deleted file mode 100644 index b152c1b2..00000000 --- a/src/libsysprof/sysprof-spawnable.c +++ /dev/null @@ -1,340 +0,0 @@ -/* sysprof-spawnable.c - * - * Copyright 2019 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 G_LOG_DOMAIN "sysprof-spawnable" - -#include "config.h" - -#include - -#include "sysprof-spawnable.h" - -typedef struct -{ - gint dest_fd; - gint fd; -} FDMapping; - -struct _SysprofSpawnable -{ - GObject parent_instance; - GArray *fds; - GPtrArray *argv; - char **environ; - char *cwd; - gint next_fd; - GSubprocessFlags flags; -}; - -G_DEFINE_TYPE (SysprofSpawnable, sysprof_spawnable, G_TYPE_OBJECT) - -/** - * sysprof_spawnable_new: - * - * Create a new #SysprofSpawnable. - * - * Returns: (transfer full): a newly created #SysprofSpawnable - */ -SysprofSpawnable * -sysprof_spawnable_new (void) -{ - return g_object_new (SYSPROF_TYPE_SPAWNABLE, NULL); -} - -/** - * sysprof_spawnable_get_flags: - * @self: a #SysprofSpawnable - * - * Gets the subprocess flags for spawning. - * - * Returns: the #GSubprocessFlags bitwise-or'd - * - * Since: 3.46 - */ -GSubprocessFlags -sysprof_spawnable_get_flags (SysprofSpawnable *self) -{ - g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), 0); - - return self->flags; -} - -/** - * sysprof_spawnable_set_flags: - * @self: a #SysprofSpawnable - * - * Set the flags to use when spawning the process. - * - * Since: 3.46 - */ -void -sysprof_spawnable_set_flags (SysprofSpawnable *self, - GSubprocessFlags flags) -{ - g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); - - self->flags = flags; -} - -static void -fd_mapping_clear (gpointer data) -{ - FDMapping *map = data; - - if (map->fd != -1) - close (map->fd); -} - -static void -sysprof_spawnable_finalize (GObject *object) -{ - SysprofSpawnable *self = (SysprofSpawnable *)object; - - g_clear_pointer (&self->fds, g_array_unref); - g_clear_pointer (&self->argv, g_ptr_array_unref); - g_clear_pointer (&self->environ, g_strfreev); - - G_OBJECT_CLASS (sysprof_spawnable_parent_class)->finalize (object); -} - -static void -sysprof_spawnable_class_init (SysprofSpawnableClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_spawnable_finalize; -} - -static void -sysprof_spawnable_init (SysprofSpawnable *self) -{ - self->next_fd = 3; - - self->argv = g_ptr_array_new_with_free_func (g_free); - g_ptr_array_add (self->argv, NULL); - - self->fds = g_array_new (FALSE, FALSE, sizeof (FDMapping)); - g_array_set_clear_func (self->fds, fd_mapping_clear); -} - -void -sysprof_spawnable_prepend_argv (SysprofSpawnable *self, - const gchar *argv) -{ - g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); - - if (argv != NULL) - g_ptr_array_insert (self->argv, 0, g_strdup (argv)); -} - -void -sysprof_spawnable_append_argv (SysprofSpawnable *self, - const gchar *argv) -{ - gint pos; - - g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); - - if (argv == NULL) - return; - - pos = self->argv->len - 1; - g_ptr_array_add (self->argv, NULL); - g_ptr_array_index (self->argv, pos) = g_strdup (argv); -} - -void -sysprof_spawnable_append_args (SysprofSpawnable *self, - const gchar * const *args) -{ - g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); - - if (args == NULL) - return; - - for (guint i = 0; args[i]; i++) - sysprof_spawnable_append_argv (self, args[i]); -} - -const gchar * const * -sysprof_spawnable_get_argv (SysprofSpawnable *self) -{ - g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), NULL); - - return (const gchar * const *)(gpointer)self->argv->pdata; -} - -const gchar * const * -sysprof_spawnable_get_environ (SysprofSpawnable *self) -{ - g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), NULL); - - return (const gchar * const *)self->environ; -} - -void -sysprof_spawnable_setenv (SysprofSpawnable *self, - const gchar *key, - const gchar *value) -{ - g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); - g_return_if_fail (key != NULL); - - self->environ = g_environ_setenv (self->environ, key, value, TRUE); -} - -void -sysprof_spawnable_set_environ (SysprofSpawnable *self, - const gchar * const *environ_) -{ - g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); - - if (environ_ != (const gchar * const *)self->environ) - { - g_strfreev (self->environ); - self->environ = g_strdupv ((gchar **)environ_); - } -} - -const gchar * -sysprof_spawnable_getenv (SysprofSpawnable *self, - const gchar *key) -{ - g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), NULL); - g_return_val_if_fail (key != NULL, NULL); - - return g_environ_getenv (self->environ, key); -} - -gint -sysprof_spawnable_take_fd (SysprofSpawnable *self, - gint fd, - gint dest_fd) -{ - FDMapping map; - - g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), -1); - - if (dest_fd < 0) - dest_fd = self->next_fd++; - - map.dest_fd = dest_fd; - map.fd = fd; - - if (dest_fd >= self->next_fd) - self->next_fd = dest_fd + 1; - - g_array_append_val (self->fds, map); - - return dest_fd; -} - -void -sysprof_spawnable_foreach_fd (SysprofSpawnable *self, - SysprofSpawnableFDForeach foreach, - gpointer user_data) -{ - g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); - g_return_if_fail (foreach != NULL); - - for (guint i = 0; i < self->fds->len; i++) - { - const FDMapping *map = &g_array_index (self->fds, FDMapping, i); - - foreach (map->dest_fd, map->fd, user_data); - } -} - -/** - * sysprof_spawnable_set_starting_fd: - * - * Sets the next FD number to use when mapping a child FD. This helps - * in situations where the embedder knows that some lower-numbered FDs - * will be taken and therefore unknown to the spawnable. - * - * The default for this is 2. - * - * Since: 3.34 - */ -void -sysprof_spawnable_set_starting_fd (SysprofSpawnable *self, - gint starting_fd) -{ - g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); - - if (starting_fd < 0) - starting_fd = 2; - - self->next_fd = starting_fd; -} - -/** - * sysprof_spawnable_spawn: - * - * Creates a new subprocess using the configured options. - * - * Returns: (transfer full): a #GSubprocess or %NULL on failure and - * @error is set. - * - * Since: 3.34 - */ -GSubprocess * -sysprof_spawnable_spawn (SysprofSpawnable *self, - GError **error) -{ - g_autoptr(GSubprocessLauncher) launcher = NULL; - const gchar * const *argv; - - g_return_val_if_fail (SYSPROF_IS_SPAWNABLE (self), NULL); - - launcher = g_subprocess_launcher_new (self->flags); - - g_subprocess_launcher_set_environ (launcher, self->environ); - - if (self->cwd != NULL) - g_subprocess_launcher_set_cwd (launcher, self->cwd); - else - g_subprocess_launcher_set_cwd (launcher, g_get_home_dir ()); - - for (guint i = 0; i < self->fds->len; i++) - { - FDMapping *map = &g_array_index (self->fds, FDMapping, i); - - g_subprocess_launcher_take_fd (launcher, map->fd, map->dest_fd); - map->fd = -1; - } - - argv = sysprof_spawnable_get_argv (self); - - return g_subprocess_launcher_spawnv (launcher, argv, error); -} - -void -sysprof_spawnable_set_cwd (SysprofSpawnable *self, - const gchar *cwd) -{ - g_return_if_fail (SYSPROF_IS_SPAWNABLE (self)); - - if (g_strcmp0 (cwd, self->cwd) != 0) - { - g_free (self->cwd); - self->cwd = g_strdup (cwd); - } -} diff --git a/src/libsysprof/sysprof-spawnable.h b/src/libsysprof/sysprof-spawnable.h deleted file mode 100644 index e6d835bf..00000000 --- a/src/libsysprof/sysprof-spawnable.h +++ /dev/null @@ -1,86 +0,0 @@ -/* sysprof-spawnable.h - * - * Copyright 2019 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 - */ - -#pragma once - -#include - -#include "sysprof-version-macros.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_SPAWNABLE (sysprof_spawnable_get_type()) - -typedef void (*SysprofSpawnableFDForeach) (gint dest_fd, - gint fd, - gpointer user_data); - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofSpawnable, sysprof_spawnable, SYSPROF, SPAWNABLE, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSpawnable *sysprof_spawnable_new (void); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_spawnable_prepend_argv (SysprofSpawnable *self, - const gchar *argv); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_spawnable_append_argv (SysprofSpawnable *self, - const gchar *argv); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_spawnable_append_args (SysprofSpawnable *self, - const gchar * const *argv); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_spawnable_set_cwd (SysprofSpawnable *self, - const gchar *cwd); -SYSPROF_AVAILABLE_IN_ALL -const gchar * const *sysprof_spawnable_get_argv (SysprofSpawnable *self); -SYSPROF_AVAILABLE_IN_ALL -const gchar * const *sysprof_spawnable_get_environ (SysprofSpawnable *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_spawnable_set_environ (SysprofSpawnable *self, - const gchar * const *environ); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_spawnable_setenv (SysprofSpawnable *self, - const gchar *key, - const gchar *value); -SYSPROF_AVAILABLE_IN_ALL -const gchar *sysprof_spawnable_getenv (SysprofSpawnable *self, - const gchar *key); -SYSPROF_AVAILABLE_IN_ALL -gint sysprof_spawnable_take_fd (SysprofSpawnable *self, - gint fd, - gint dest_fd); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_spawnable_foreach_fd (SysprofSpawnable *self, - SysprofSpawnableFDForeach foreach, - gpointer user_data); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_spawnable_set_starting_fd (SysprofSpawnable *self, - gint starting_fd); -SYSPROF_AVAILABLE_IN_ALL -GSubprocess *sysprof_spawnable_spawn (SysprofSpawnable *self, - GError **error); -SYSPROF_AVAILABLE_IN_3_46 -GSubprocessFlags sysprof_spawnable_get_flags (SysprofSpawnable *self); -SYSPROF_AVAILABLE_IN_3_46 -void sysprof_spawnable_set_flags (SysprofSpawnable *self, - GSubprocessFlags flags); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-symbol-map.c b/src/libsysprof/sysprof-symbol-map.c deleted file mode 100644 index 2ff57d54..00000000 --- a/src/libsysprof/sysprof-symbol-map.c +++ /dev/null @@ -1,593 +0,0 @@ -/* sysprof-symbol-map.c - * - * Copyright 2019 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 G_LOG_DOMAIN "sysprof-symbol-map" - -#include "config.h" - -#include -#include -#include - -#include "sysprof-map-lookaside.h" -#include "sysprof-symbol-map.h" - -/* - * Because we can't rely on the address ranges of symbols from ELF files - * or elsewhere, we have to duplicate a lot of entries when building this - * so that we can resolve all of the corrent addresses. - */ - -SYSPROF_ALIGNED_BEGIN(1) -typedef struct -{ - SysprofCaptureAddress addr_begin; - SysprofCaptureAddress addr_end; - guint32 pid; - guint32 offset; - guint32 tag_offset; - guint32 padding; -} Decoded -SYSPROF_ALIGNED_END(1); - -struct _SysprofSymbolMap -{ - /* For creating maps */ - GStringChunk *chunk; - GHashTable *lookasides; - GPtrArray *resolvers; - GPtrArray *samples; - guint resolved : 1; - - /* For reading maps */ - GMappedFile *mapped; - const Decoded *symbols; - gsize n_symbols; - const gchar *beginptr; - const gchar *endptr; -}; - -typedef struct -{ - SysprofCaptureAddress addr; - const gchar *name; - GQuark tag; - guint32 pid; -} Element; - -static void -element_free (Element *ele) -{ - g_slice_free (Element, ele); -} - -static gint -element_compare (gconstpointer a, - gconstpointer b) -{ - const Element *aa = *(const Element **)a; - const Element *bb = *(const Element **)b; - - if (aa->pid < bb->pid) - return -1; - - if (aa->pid > bb->pid) - return 1; - - if (aa->addr < bb->addr) - return -1; - - if (aa->addr > bb->addr) - return 1; - - return 0; -} - -static guint -element_hash (gconstpointer data) -{ - const Element *ele = data; - struct { - guint32 a; - guint32 b; - } addr; - - memcpy (&addr, &ele->addr, sizeof addr); - return addr.a ^ addr.b ^ ele->pid; -} - -static gboolean -element_equal (gconstpointer a, - gconstpointer b) -{ - const Element *aa = a; - const Element *bb = b; - - return aa->pid == bb->pid && aa->addr == bb->addr; -} - -SysprofSymbolMap * -sysprof_symbol_map_new (void) -{ - SysprofSymbolMap *self; - - self = g_slice_new0 (SysprofSymbolMap); - self->samples = g_ptr_array_new_with_free_func ((GDestroyNotify) element_free); - self->chunk = g_string_chunk_new (4096*16); - self->resolvers = g_ptr_array_new_with_free_func (g_object_unref); - self->lookasides = g_hash_table_new_full (NULL, NULL, NULL, - (GDestroyNotify) sysprof_map_lookaside_free); - - return g_steal_pointer (&self); -} - -void -sysprof_symbol_map_free (SysprofSymbolMap *self) -{ - g_clear_pointer (&self->lookasides, g_hash_table_unref); - g_clear_pointer (&self->resolvers, g_ptr_array_unref); - g_clear_pointer (&self->chunk, g_string_chunk_free); - g_clear_pointer (&self->samples, g_ptr_array_unref); - g_clear_pointer (&self->mapped, g_mapped_file_unref); - self->beginptr = NULL; - self->endptr = NULL; - self->symbols = NULL; - self->n_symbols = 0; - g_slice_free (SysprofSymbolMap, self); -} - -static gint -search_for_symbol_cb (gconstpointer a, - gconstpointer b) -{ - const Decoded *key = a; - const Decoded *ele = b; - - if (key->pid < ele->pid) - return -1; - - if (key->pid > ele->pid) - return 1; - - g_assert (key->pid == ele->pid); - - if (key->addr_begin < ele->addr_begin) - return -1; - - if (key->addr_begin > ele->addr_end) - return 1; - - g_assert (key->addr_begin >= ele->addr_begin); - g_assert (key->addr_end <= ele->addr_end); - - return 0; -} - -const gchar * -sysprof_symbol_map_lookup (SysprofSymbolMap *self, - gint64 time, - gint32 pid, - SysprofCaptureAddress addr, - GQuark *tag) -{ - const Decoded *ret; - const Decoded key = { - .addr_begin = addr, - .addr_end = addr, - .pid = pid, - .offset = 0, - .tag_offset = 0, - }; - - g_assert (self != NULL); - - if (tag != NULL) - *tag = 0; - - ret = bsearch (&key, - self->symbols, - self->n_symbols, - sizeof *ret, - search_for_symbol_cb); - - if (ret == NULL || ret->offset == 0) - return NULL; - - if (tag != NULL && ret->tag_offset > 0) - { - if (ret->tag_offset < (self->endptr - self->beginptr)) - *tag = g_quark_from_string (&self->beginptr[ret->tag_offset]); - } - - if (ret->offset < (self->endptr - self->beginptr)) - return &self->beginptr[ret->offset]; - - return NULL; -} - -void -sysprof_symbol_map_add_resolver (SysprofSymbolMap *self, - SysprofSymbolResolver *resolver) -{ - g_assert (self != NULL); - g_assert (SYSPROF_IS_SYMBOL_RESOLVER (resolver)); - - g_ptr_array_add (self->resolvers, g_object_ref (resolver)); -} - -static gboolean -sysprof_symbol_map_do_alloc (SysprofSymbolMap *self, - SysprofCaptureReader *reader, - GHashTable *seen) -{ - const SysprofCaptureAllocation *ev; - - g_assert (self != NULL); - g_assert (reader != NULL); - g_assert (seen != NULL); - - if (!(ev = sysprof_capture_reader_read_allocation (reader))) - return FALSE; - - for (guint i = 0; i < ev->n_addrs; i++) - { - SysprofCaptureAddress addr = ev->addrs[i]; - - for (guint j = 0; j < self->resolvers->len; j++) - { - SysprofSymbolResolver *resolver = g_ptr_array_index (self->resolvers, j); - g_autofree gchar *name = NULL; - const gchar *cname; - Element ele; - GQuark tag = 0; - - name = sysprof_symbol_resolver_resolve_with_context (resolver, - ev->frame.time, - ev->frame.pid, - SYSPROF_ADDRESS_CONTEXT_USER, - addr, - &tag); - - if (name == NULL) - continue; - - cname = g_string_chunk_insert_const (self->chunk, name); - - ele.addr = addr; - ele.pid = ev->frame.pid; - ele.name = cname; - ele.tag = tag; - - if (!g_hash_table_contains (seen, &ele)) - { - Element *cpy = g_slice_dup (Element, &ele); - g_hash_table_add (seen, cpy); - g_ptr_array_add (self->samples, cpy); - } - } - } - - return TRUE; -} - -static gboolean -sysprof_symbol_map_do_sample (SysprofSymbolMap *self, - SysprofCaptureReader *reader, - GHashTable *seen) -{ - SysprofAddressContext last_context = SYSPROF_ADDRESS_CONTEXT_NONE; - const SysprofCaptureSample *sample; - - g_assert (self != NULL); - g_assert (reader != NULL); - g_assert (seen != NULL); - - if (!(sample = sysprof_capture_reader_read_sample (reader))) - return FALSE; - - for (guint i = 0; i < sample->n_addrs; i++) - { - SysprofCaptureAddress addr = sample->addrs[i]; - SysprofAddressContext context; - - if (sysprof_address_is_context_switch (addr, &context)) - { - last_context = context; - continue; - } - - /* Handle backtrace() style backtraces with no context switch */ - if (last_context == SYSPROF_ADDRESS_CONTEXT_NONE) - last_context = SYSPROF_ADDRESS_CONTEXT_USER; - - for (guint j = 0; j < self->resolvers->len; j++) - { - SysprofSymbolResolver *resolver = g_ptr_array_index (self->resolvers, j); - g_autofree gchar *name = NULL; - const gchar *cname; - Element ele; - GQuark tag = 0; - - name = sysprof_symbol_resolver_resolve_with_context (resolver, - sample->frame.time, - sample->frame.pid, - last_context, - addr, - &tag); - - if (name == NULL) - continue; - - cname = g_string_chunk_insert_const (self->chunk, name); - - ele.addr = addr; - ele.pid = sample->frame.pid; - ele.name = cname; - ele.tag = tag; - - if (!g_hash_table_contains (seen, &ele)) - { - Element *cpy = g_slice_dup (Element, &ele); - g_hash_table_add (seen, cpy); - g_ptr_array_add (self->samples, cpy); - } - } - } - - return TRUE; -} - -void -sysprof_symbol_map_resolve (SysprofSymbolMap *self, - SysprofCaptureReader *reader) -{ - g_autoptr(GHashTable) seen = NULL; - SysprofCaptureFrameType type; - - g_return_if_fail (self != NULL); - g_return_if_fail (self->resolved == FALSE); - g_return_if_fail (reader != NULL); - - self->resolved = TRUE; - - seen = g_hash_table_new (element_hash, element_equal); - - sysprof_capture_reader_reset (reader); - - for (guint i = 0; i < self->resolvers->len; i++) - { - sysprof_symbol_resolver_load (g_ptr_array_index (self->resolvers, i), reader); - sysprof_capture_reader_reset (reader); - } - - while (sysprof_capture_reader_peek_type (reader, &type)) - { - if (type == SYSPROF_CAPTURE_FRAME_SAMPLE) - { - if (!sysprof_symbol_map_do_sample (self, reader, seen)) - break; - continue; - } - else if (type == SYSPROF_CAPTURE_FRAME_ALLOCATION) - { - if (!sysprof_symbol_map_do_alloc (self, reader, seen)) - break; - continue; - } - - if (!sysprof_capture_reader_skip (reader)) - break; - } - - g_ptr_array_sort (self->samples, element_compare); -} - -void -sysprof_symbol_map_printf (SysprofSymbolMap *self) -{ - g_return_if_fail (self != NULL); - g_return_if_fail (self->samples != NULL); - - for (guint i = 0; i < self->samples->len; i++) - { - Element *ele = g_ptr_array_index (self->samples, i); - - if (ele->tag) - g_print ("%-5d: %"G_GUINT64_FORMAT": %s [%s]\n", ele->pid, ele->addr, ele->name, g_quark_to_string (ele->tag)); - else - g_print ("%-5d: %"G_GUINT64_FORMAT": %s\n", ele->pid, ele->addr, ele->name); - } -} - -static guint -get_string_offset (GByteArray *ar, - GHashTable *seen, - const gchar *str) -{ - gpointer ret; - - if (str == NULL) - return 0; - - if G_UNLIKELY (!g_hash_table_lookup_extended (seen, str, NULL, &ret)) - { - ret = GUINT_TO_POINTER (ar->len); - g_byte_array_append (ar, (guint8 *)str, strlen (str) + 1); - g_hash_table_insert (seen, (gpointer)str, ret); - } - - return GPOINTER_TO_UINT (ret); -} - -gboolean -sysprof_symbol_map_serialize (SysprofSymbolMap *self, - gint fd) -{ - static const Decoded empty = {0}; - SysprofCaptureAddress begin = 0; - g_autoptr(GByteArray) ar = NULL; - g_autoptr(GHashTable) seen = NULL; - g_autoptr(GArray) decoded = NULL; - gsize offset; - - g_assert (self != NULL); - g_assert (fd != -1); - - ar = g_byte_array_new (); - seen = g_hash_table_new (NULL, NULL); - decoded = g_array_new (FALSE, FALSE, sizeof (Decoded)); - - /* Add some empty space to both give us non-zero offsets and also ensure - * empty space between data. - */ - g_byte_array_append (ar, (guint8 *)&empty, sizeof empty); - - for (guint i = 0; i < self->samples->len; i++) - { - Element *ele = g_ptr_array_index (self->samples, i); - Decoded dec; - - if (begin == 0) - begin = ele->addr; - - if ((i + 1) < self->samples->len) - { - Element *next = g_ptr_array_index (self->samples, i + 1); - - if (ele->pid == next->pid && ele->name == next->name) - continue; - } - - dec.padding = 0; - dec.addr_begin = begin; - dec.addr_end = ele->addr; - dec.pid = ele->pid; - dec.offset = get_string_offset (ar, seen, ele->name); - - g_assert (!dec.offset || g_strcmp0 (ele->name, (gchar *)&ar->data[dec.offset]) == 0); - - if (ele->tag) - { - const gchar *tagstr = g_quark_to_string (ele->tag); - - dec.tag_offset = get_string_offset (ar, seen, tagstr); - g_assert (g_strcmp0 (tagstr, (gchar *)&ar->data[dec.tag_offset]) == 0); - } - else - dec.tag_offset = 0; - - g_array_append_val (decoded, dec); - - begin = 0; - } - - offset = sizeof (Decoded) * (gsize)decoded->len; - - for (guint i = 0; i < decoded->len; i++) - { - Decoded *dec = &g_array_index (decoded, Decoded, i); - - if (dec->offset > 0) - dec->offset += offset; - - if (dec->tag_offset > 0) - dec->tag_offset += offset; - } - - if (write (fd, decoded->data, offset) != offset) - return FALSE; - - if (write (fd, ar->data, ar->len) != ar->len) - return FALSE; - - /* Aggressively release state now that we're finished */ - if (self->samples->len) - g_ptr_array_remove_range (self->samples, 0, self->samples->len); - if (self->resolvers != NULL) - g_ptr_array_remove_range (self->resolvers, 0, self->resolvers->len); - g_string_chunk_clear (self->chunk); - g_hash_table_remove_all (self->lookasides); - - lseek (fd, 0L, SEEK_SET); - - return TRUE; -} - -gboolean -sysprof_symbol_map_deserialize (SysprofSymbolMap *self, - gint byte_order, - gint fd) -{ - g_autoptr(GError) error = NULL; - gboolean needs_swap = byte_order != G_BYTE_ORDER; - gchar *beginptr; - gchar *endptr; - - g_return_val_if_fail (self != NULL, FALSE); - g_return_val_if_fail (self->mapped == NULL, FALSE); - - if (!(self->mapped = g_mapped_file_new_from_fd (fd, TRUE, &error))) - { - g_warning ("Failed to map file: %s\n", error->message); - return FALSE; - } - - beginptr = g_mapped_file_get_contents (self->mapped); - endptr = beginptr + g_mapped_file_get_length (self->mapped); - - /* Ensure trialing \0 */ - if (endptr > beginptr) - *(endptr - 1) = 0; - - for (gchar *ptr = beginptr; - ptr < endptr && (ptr + sizeof (Decoded)) < endptr; - ptr += sizeof (Decoded)) - { - Decoded *sym = (Decoded *)ptr; - - if (sym->addr_begin == 0 && - sym->addr_end == 0 && - sym->pid == 0 && - sym->offset == 0) - { - self->symbols = (const Decoded *)beginptr; - self->n_symbols = sym - self->symbols; - break; - } - else if (needs_swap) - { - sym->addr_begin = GUINT64_SWAP_LE_BE (sym->addr_begin); - sym->addr_end = GUINT64_SWAP_LE_BE (sym->addr_end); - sym->pid = GUINT32_SWAP_LE_BE (sym->pid); - sym->offset = GUINT32_SWAP_LE_BE (sym->offset); - sym->tag_offset = GUINT32_SWAP_LE_BE (sym->tag_offset); - } - -#if 0 - g_print ("Added pid=%d begin=%p end=%p\n", - sym->pid, (gpointer)sym->addr_begin, (gpointer)sym->addr_end); -#endif - } - - self->beginptr = beginptr; - self->endptr = endptr; - - return TRUE; -} diff --git a/src/libsysprof/sysprof-symbol-map.h b/src/libsysprof/sysprof-symbol-map.h deleted file mode 100644 index 2e482c2c..00000000 --- a/src/libsysprof/sysprof-symbol-map.h +++ /dev/null @@ -1,49 +0,0 @@ -/* sysprof-symbol-map.h - * - * Copyright 2019 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 - */ - -#pragma once - -#include - -#include "sysprof-symbol-resolver.h" - -G_BEGIN_DECLS - -typedef struct _SysprofSymbolMap SysprofSymbolMap; - -SysprofSymbolMap *sysprof_symbol_map_new (void); -void sysprof_symbol_map_add_resolver (SysprofSymbolMap *self, - SysprofSymbolResolver *resolver); -void sysprof_symbol_map_resolve (SysprofSymbolMap *self, - SysprofCaptureReader *reader); -const gchar *sysprof_symbol_map_lookup (SysprofSymbolMap *self, - gint64 time, - gint32 pid, - SysprofCaptureAddress addr, - GQuark *tag); -void sysprof_symbol_map_printf (SysprofSymbolMap *self); -gboolean sysprof_symbol_map_serialize (SysprofSymbolMap *self, - gint fd); -gboolean sysprof_symbol_map_deserialize (SysprofSymbolMap *self, - gint byte_order, - gint fd); -void sysprof_symbol_map_free (SysprofSymbolMap *self); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-symbol-resolver-private.h b/src/libsysprof/sysprof-symbol-resolver-private.h deleted file mode 100644 index 76cbe45e..00000000 --- a/src/libsysprof/sysprof-symbol-resolver-private.h +++ /dev/null @@ -1,31 +0,0 @@ -/* sysprof-symbol-resolver-private.h - * - * Copyright 2021 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 - */ - -#pragma once - -#include -#include - -G_BEGIN_DECLS - -char *_sysprof_symbol_resolver_load_file (SysprofCaptureReader *reader, - const char *path); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-symbol-resolver.c b/src/libsysprof/sysprof-symbol-resolver.c deleted file mode 100644 index 3ba6166d..00000000 --- a/src/libsysprof/sysprof-symbol-resolver.c +++ /dev/null @@ -1,189 +0,0 @@ -/* sysprof-symbol-resolver.c - * - * Copyright 2016-2019 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 - */ - -#include "config.h" - -#include - -#include "sysprof-platform.h" -#include "sysprof-symbol-resolver.h" - -G_DEFINE_INTERFACE (SysprofSymbolResolver, sysprof_symbol_resolver, G_TYPE_OBJECT) - -static gchar * -sysprof_symbol_resolver_real_resolve (SysprofSymbolResolver *self, - guint64 time, - GPid pid, - SysprofCaptureAddress address, - GQuark *tag) -{ - *tag = 0; - return NULL; -} - -static gchar * -sysprof_symbol_resolver_real_resolve_with_context (SysprofSymbolResolver *self, - guint64 time, - GPid pid, - SysprofAddressContext context, - SysprofCaptureAddress address, - GQuark *tag) -{ - *tag = 0; - - if (SYSPROF_SYMBOL_RESOLVER_GET_IFACE (self)->resolve) - return SYSPROF_SYMBOL_RESOLVER_GET_IFACE (self)->resolve (self, time, pid, address, tag); - - return NULL; -} - -static void -sysprof_symbol_resolver_default_init (SysprofSymbolResolverInterface *iface) -{ - iface->resolve = sysprof_symbol_resolver_real_resolve; - iface->resolve_with_context = sysprof_symbol_resolver_real_resolve_with_context; -} - -void -sysprof_symbol_resolver_load (SysprofSymbolResolver *self, - SysprofCaptureReader *reader) -{ - g_return_if_fail (SYSPROF_IS_SYMBOL_RESOLVER (self)); - g_return_if_fail (reader != NULL); - - if (SYSPROF_SYMBOL_RESOLVER_GET_IFACE (self)->load) - SYSPROF_SYMBOL_RESOLVER_GET_IFACE (self)->load (self, reader); -} - -/** - * sysprof_symbol_resolver_resolve: - * @self: A #SysprofSymbolResolver - * @time: The time of the sample - * @pid: The process generating the sample - * @address: the sample address - * @tag: (out): A tag for the symbol. - * - * Gets the symbol name for @address that was part of process @pid - * at @time. Optionally, you can set @tag to a quark describing the - * symbol. This can be used to provide a bit more information when - * rendering the treeview. You might choose to describe the library - * such as "GObject" or "GTK+" or "Linux" for the kernel. - * - * Returns: (nullable) (transfer full): A newly allocated string, or %NULL. - */ -gchar * -sysprof_symbol_resolver_resolve (SysprofSymbolResolver *self, - guint64 time, - GPid pid, - SysprofCaptureAddress address, - GQuark *tag) -{ - GQuark dummy; - - g_return_val_if_fail (SYSPROF_IS_SYMBOL_RESOLVER (self), NULL); - - if (tag == NULL) - tag = &dummy; - - *tag = 0; - - if (SYSPROF_SYMBOL_RESOLVER_GET_IFACE (self)->resolve) - return SYSPROF_SYMBOL_RESOLVER_GET_IFACE (self)->resolve (self, time, pid, address, tag); - - return NULL; -} - -/** - * sysprof_symbol_resolver_resolve_with_context: - * @self: A #SysprofSymbolResolver - * @time: The time of the sample - * @pid: The process generating the sample - * @context: the address context - * @address: the sample address - * @tag: (out): A tag for the symbol. - * - * This function is like sysprof_symbol_resolver_resolve() but allows - * access to the address context, which might be necessary to - * determine the difference between user space and kernel space - * addresses. - * - * Returns: (nullable) (transfer full): A newly allocated string, or %NULL. - */ -gchar * -sysprof_symbol_resolver_resolve_with_context (SysprofSymbolResolver *self, - guint64 time, - GPid pid, - SysprofAddressContext context, - SysprofCaptureAddress address, - GQuark *tag) -{ - GQuark dummy; - - g_return_val_if_fail (SYSPROF_IS_SYMBOL_RESOLVER (self), NULL); - - if (tag == NULL) - tag = &dummy; - - *tag = 0; - - return SYSPROF_SYMBOL_RESOLVER_GET_IFACE (self)->resolve_with_context (self, time, pid, context, address, tag); -} - -char * -_sysprof_symbol_resolver_load_file (SysprofCaptureReader *reader, - const char *path) -{ - g_autofree char *data = NULL; - goffset len; - goffset pos = 0; - int fd; - - g_assert (reader != NULL); - g_assert (path != NULL); - - sysprof_capture_reader_reset (reader); - - if (-1 == (fd = sysprof_memfd_create ("")) || - !sysprof_capture_reader_read_file_fd (reader, path, fd)) - { - if (fd != -1) - close (fd); - return NULL; - } - - len = lseek (fd, 0L, SEEK_CUR); - data = g_malloc (len + 1); - lseek (fd, 0L, SEEK_SET); - - while (pos < len) - { - gssize n_read = read (fd, data + pos, len - pos); - - if (n_read < 0) - return NULL; - - pos += n_read; - } - - data[len] = 0; - close (fd); - - return g_steal_pointer (&data); -} diff --git a/src/libsysprof/sysprof-symbol-resolver.h b/src/libsysprof/sysprof-symbol-resolver.h deleted file mode 100644 index fcdceebb..00000000 --- a/src/libsysprof/sysprof-symbol-resolver.h +++ /dev/null @@ -1,76 +0,0 @@ -/* sysprof-symbol-resolver.h - * - * Copyright 2016-2019 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 - */ - -#pragma once - -#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION) -# error "Only can be included directly." -#endif - -#include - -#include "sysprof-address.h" -#include "sysprof-capture-reader.h" -#include "sysprof-version-macros.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_SYMBOL_RESOLVER (sysprof_symbol_resolver_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_INTERFACE (SysprofSymbolResolver, sysprof_symbol_resolver, SYSPROF, SYMBOL_RESOLVER, GObject) - -struct _SysprofSymbolResolverInterface -{ - GTypeInterface parent_interface; - - void (*load) (SysprofSymbolResolver *self, - SysprofCaptureReader *reader); - gchar *(*resolve) (SysprofSymbolResolver *self, - guint64 time, - GPid pid, - SysprofCaptureAddress address, - GQuark *tag); - gchar *(*resolve_with_context) (SysprofSymbolResolver *self, - guint64 time, - GPid pid, - SysprofAddressContext context, - SysprofCaptureAddress address, - GQuark *tag); -}; - -SYSPROF_AVAILABLE_IN_ALL -void sysprof_symbol_resolver_load (SysprofSymbolResolver *self, - SysprofCaptureReader *reader); -SYSPROF_AVAILABLE_IN_ALL -gchar *sysprof_symbol_resolver_resolve (SysprofSymbolResolver *self, - guint64 time, - GPid pid, - SysprofCaptureAddress address, - GQuark *tag); -SYSPROF_AVAILABLE_IN_ALL -gchar *sysprof_symbol_resolver_resolve_with_context (SysprofSymbolResolver *self, - guint64 time, - GPid pid, - SysprofAddressContext context, - SysprofCaptureAddress address, - GQuark *tag); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-symbols-source.c b/src/libsysprof/sysprof-symbols-source.c deleted file mode 100644 index b4d7d49a..00000000 --- a/src/libsysprof/sysprof-symbols-source.c +++ /dev/null @@ -1,168 +0,0 @@ -/* sysprof-symbols-source.c - * - * Copyright 2019 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 G_LOG_DOMAIN "sysprof-symbols-source" - -#include "config.h" - -#include "sysprof-elf-symbol-resolver.h" -#include "sysprof-private.h" -#include "sysprof-symbol-map.h" -#include "sysprof-symbols-source.h" - -#include "sysprof-platform.h" - -struct _SysprofSymbolsSource -{ - GObject parent_instance; - SysprofCaptureWriter *writer; - guint user_only : 1; -}; - -static void source_iface_init (SysprofSourceInterface *iface); - -G_DEFINE_TYPE_WITH_CODE (SysprofSymbolsSource, sysprof_symbols_source, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init)) - -static void -sysprof_symbols_source_finalize (GObject *object) -{ - SysprofSymbolsSource *self = (SysprofSymbolsSource *)object; - - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); - - G_OBJECT_CLASS (sysprof_symbols_source_parent_class)->finalize (object); -} - -static void -sysprof_symbols_source_class_init (SysprofSymbolsSourceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_symbols_source_finalize; -} - -static void -sysprof_symbols_source_init (SysprofSymbolsSource *self) -{ -} - -static void -sysprof_symbols_source_set_writer (SysprofSource *source, - SysprofCaptureWriter *writer) -{ - SysprofSymbolsSource *self = (SysprofSymbolsSource *)source; - - g_assert (SYSPROF_IS_SYMBOLS_SOURCE (self)); - g_assert (writer != NULL); - - g_clear_pointer (&self->writer, sysprof_capture_writer_unref); - self->writer = sysprof_capture_writer_ref (writer); -} - -static void -sysprof_symbols_source_supplement (SysprofSource *source, - SysprofCaptureReader *reader) -{ - SysprofSymbolsSource *self = (SysprofSymbolsSource *)source; - g_autoptr(SysprofSymbolResolver) native = NULL; - g_autoptr(SysprofSymbolResolver) kernel = NULL; - SysprofSymbolMap *map; - gint fd; - - g_assert (SYSPROF_IS_SYMBOLS_SOURCE (self)); - g_assert (reader != NULL); - g_assert (self->writer != NULL); - - if (-1 == (fd = sysprof_memfd_create ("[sysprof-decode]"))) - return; - - map = sysprof_symbol_map_new (); - - native = sysprof_elf_symbol_resolver_new (); - sysprof_symbol_map_add_resolver (map, native); - - if (!self->user_only) - { - kernel = sysprof_kernel_symbol_resolver_new (); - sysprof_symbol_map_add_resolver (map, kernel); - } - - sysprof_symbol_map_resolve (map, reader); - sysprof_symbol_map_serialize (map, fd); - sysprof_symbol_map_free (map); - - sysprof_capture_writer_add_file_fd (self->writer, - SYSPROF_CAPTURE_CURRENT_TIME, - -1, - -1, - "__symbols__", - fd); - - close (fd); -} - -static void -sysprof_symbols_source_start (SysprofSource *source) -{ - g_assert (SYSPROF_IS_SYMBOLS_SOURCE (source)); - - sysprof_source_emit_ready (source); -} - -static void -sysprof_symbols_source_stop (SysprofSource *source) -{ - g_assert (SYSPROF_IS_SYMBOLS_SOURCE (source)); - - sysprof_source_emit_finished (source); -} - -static void -source_iface_init (SysprofSourceInterface *iface) -{ - iface->set_writer = sysprof_symbols_source_set_writer; - iface->supplement = sysprof_symbols_source_supplement; - iface->start = sysprof_symbols_source_start; - iface->stop = sysprof_symbols_source_stop; -} - -SysprofSource * -sysprof_symbols_source_new (void) -{ - return g_object_new (SYSPROF_TYPE_SYMBOLS_SOURCE, NULL); -} - -void -sysprof_symbols_source_set_user_only (SysprofSymbolsSource *self, - gboolean user_only) -{ - g_return_if_fail (SYSPROF_IS_SYMBOLS_SOURCE (self)); - - self->user_only = !!user_only; -} - -gboolean -sysprof_symbols_source_get_user_only (SysprofSymbolsSource *self) -{ - g_return_val_if_fail (SYSPROF_IS_SYMBOLS_SOURCE (self), FALSE); - - return self->user_only; -} diff --git a/src/libsysprof/sysprof-symbols-source.h b/src/libsysprof/sysprof-symbols-source.h deleted file mode 100644 index c78bb3c1..00000000 --- a/src/libsysprof/sysprof-symbols-source.h +++ /dev/null @@ -1,40 +0,0 @@ -/* sysprof-symbols-source.h - * - * Copyright 2019 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 - */ - -#pragma once - -#include "sysprof-source.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_SYMBOLS_SOURCE (sysprof_symbols_source_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofSymbolsSource, sysprof_symbols_source, SYSPROF, SYMBOLS_SOURCE, GObject) - -SYSPROF_AVAILABLE_IN_ALL -SysprofSource *sysprof_symbols_source_new (void); -SYSPROF_AVAILABLE_IN_3_36 -void sysprof_symbols_source_set_user_only (SysprofSymbolsSource *self, - gboolean user_only); -SYSPROF_AVAILABLE_IN_3_36 -gboolean sysprof_symbols_source_get_user_only (SysprofSymbolsSource *self); - -G_END_DECLS diff --git a/src/libsysprof/sysprof-tracefd-source.c b/src/libsysprof/sysprof-tracefd-source.c deleted file mode 100644 index 72c9fb05..00000000 --- a/src/libsysprof/sysprof-tracefd-source.c +++ /dev/null @@ -1,302 +0,0 @@ -/* sysprof-tracefd-source.c - * - * Copyright 2019 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 G_LOG_DOMAIN "sysprof-tracefd-source" - -#include "config.h" - -#include -#include - -#include "sysprof-capture-autocleanups.h" -#include "sysprof-platform.h" -#include "sysprof-tracefd-source.h" - -typedef struct -{ - SysprofCaptureWriter *writer; - gchar *envvar; - gint tracefd; -} SysprofTracefdSourcePrivate; - -enum { - PROP_0, - PROP_ENVVAR, - N_PROPS -}; - -static void source_iface_init (SysprofSourceInterface *iface); - -G_DEFINE_TYPE_WITH_CODE (SysprofTracefdSource, sysprof_tracefd_source, G_TYPE_OBJECT, - G_ADD_PRIVATE (SysprofTracefdSource) - G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init)) - -static GParamSpec *properties [N_PROPS]; - -static void -sysprof_tracefd_source_finalize (GObject *object) -{ - SysprofTracefdSource *self = (SysprofTracefdSource *)object; - SysprofTracefdSourcePrivate *priv = sysprof_tracefd_source_get_instance_private (self); - - g_clear_pointer (&priv->writer, sysprof_capture_writer_unref); - g_clear_pointer (&priv->envvar, g_free); - - if (priv->tracefd != -1) - { - close (priv->tracefd); - priv->tracefd = -1; - } - - G_OBJECT_CLASS (sysprof_tracefd_source_parent_class)->finalize (object); -} - -static void -sysprof_tracefd_source_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofTracefdSource *self = SYSPROF_TRACEFD_SOURCE (object); - - switch (prop_id) - { - case PROP_ENVVAR: - g_value_set_string (value, sysprof_tracefd_source_get_envvar (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_tracefd_source_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofTracefdSource *self = SYSPROF_TRACEFD_SOURCE (object); - - switch (prop_id) - { - case PROP_ENVVAR: - sysprof_tracefd_source_set_envvar (self, g_value_get_string (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_tracefd_source_class_init (SysprofTracefdSourceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = sysprof_tracefd_source_finalize; - object_class->get_property = sysprof_tracefd_source_get_property; - object_class->set_property = sysprof_tracefd_source_set_property; - - properties [PROP_ENVVAR] = - g_param_spec_string ("envvar", - "Environment Variable", - "The environment variable to set", - "SYSPROF_TRACE_FD", - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_tracefd_source_init (SysprofTracefdSource *self) -{ - SysprofTracefdSourcePrivate *priv = sysprof_tracefd_source_get_instance_private (self); - - priv->tracefd = -1; - priv->envvar = g_strdup ("SYSPROF_TRACE_FD"); -} - -const gchar * -sysprof_tracefd_source_get_envvar (SysprofTracefdSource *self) -{ - SysprofTracefdSourcePrivate *priv = sysprof_tracefd_source_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_TRACEFD_SOURCE (self), NULL); - - return priv->envvar; -} - -void -sysprof_tracefd_source_set_envvar (SysprofTracefdSource *self, - const gchar *envvar) -{ - SysprofTracefdSourcePrivate *priv = sysprof_tracefd_source_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_TRACEFD_SOURCE (self)); - - if (envvar == NULL) - envvar = "SYSPROF_TRACE_FD"; - - if (g_strcmp0 (priv->envvar, envvar) != 0) - { - g_free (priv->envvar); - priv->envvar = g_strdup (envvar); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ENVVAR]); - } -} - -static void -sysprof_tracefd_source_modify_spawn (SysprofSource *source, - SysprofSpawnable *spawnable) -{ - SysprofTracefdSource *self = (SysprofTracefdSource *)source; - SysprofTracefdSourcePrivate *priv = sysprof_tracefd_source_get_instance_private (self); - g_autofree gchar *name = NULL; - g_autofree gchar *fdstr = NULL; - gint dest_fd; - gint fd; - - g_assert (SYSPROF_IS_TRACEFD_SOURCE (self)); - g_assert (SYSPROF_IS_SPAWNABLE (spawnable)); - g_assert (priv->tracefd == -1); - - name = g_strdup_printf ("[sysprof-tracefd:%s]", priv->envvar); - - if (-1 == (fd = sysprof_memfd_create (name))) - { - g_warning ("Failed to create FD for tracefd capture: %s", - g_strerror (errno)); - return; - } - - if (-1 == (priv->tracefd = dup (fd))) - { - g_warning ("Failed to duplicate tracefd for readback: %s", - g_strerror (errno)); - close (fd); - return; - } - - dest_fd = sysprof_spawnable_take_fd (spawnable, fd, -1); - fdstr = g_strdup_printf ("%u", dest_fd); - sysprof_spawnable_setenv (spawnable, priv->envvar, fdstr); -} - -static void -sysprof_tracefd_source_serialize (SysprofSource *source, - GKeyFile *keyfile, - const gchar *group) -{ - SysprofTracefdSource *self = (SysprofTracefdSource *)source; - SysprofTracefdSourcePrivate *priv = sysprof_tracefd_source_get_instance_private (self); - - g_assert (SYSPROF_IS_TRACEFD_SOURCE (self)); - g_assert (keyfile != NULL); - g_assert (group != NULL); - - g_key_file_set_string (keyfile, group, "envvar", priv->envvar); -} - -static void -sysprof_tracefd_source_deserialize (SysprofSource *source, - GKeyFile *keyfile, - const gchar *group) -{ - SysprofTracefdSource *self = (SysprofTracefdSource *)source; - g_autofree gchar *envvar = NULL; - - g_assert (SYSPROF_IS_TRACEFD_SOURCE (self)); - g_assert (keyfile != NULL); - g_assert (group != NULL); - - if ((envvar = g_key_file_get_string (keyfile, group, "envvar", NULL))) - sysprof_tracefd_source_set_envvar (self, envvar); -} - -static void -sysprof_tracefd_source_prepare (SysprofSource *source) -{ - g_assert (SYSPROF_IS_TRACEFD_SOURCE (source)); - - sysprof_source_emit_ready (source); -} - -static gboolean -sysprof_tracefd_source_get_is_ready (SysprofSource *source) -{ - g_assert (SYSPROF_IS_TRACEFD_SOURCE (source)); - - return TRUE; -} - -static void -sysprof_tracefd_source_stop (SysprofSource *source) -{ - SysprofTracefdSource *self = (SysprofTracefdSource *)source; - SysprofTracefdSourcePrivate *priv = sysprof_tracefd_source_get_instance_private (self); - - g_assert (SYSPROF_IS_TRACEFD_SOURCE (self)); - - if (priv->writer != NULL && priv->tracefd != -1) - { - g_autoptr(SysprofCaptureReader) reader = NULL; - - /* FIXME: Error handling is ignored here */ - if ((reader = sysprof_capture_reader_new_from_fd (priv->tracefd))) - sysprof_capture_writer_cat (priv->writer, reader); - - priv->tracefd = -1; - } - - sysprof_source_emit_finished (source); -} - -static void -sysprof_tracefd_source_set_writer (SysprofSource *source, - SysprofCaptureWriter *writer) -{ - SysprofTracefdSource *self = (SysprofTracefdSource *)source; - SysprofTracefdSourcePrivate *priv = sysprof_tracefd_source_get_instance_private (self); - - g_assert (SYSPROF_IS_TRACEFD_SOURCE (self)); - g_assert (writer != NULL); - - g_clear_pointer (&priv->writer, sysprof_capture_writer_unref); - priv->writer = sysprof_capture_writer_ref (writer); -} - -static void -source_iface_init (SysprofSourceInterface *iface) -{ - iface->deserialize = sysprof_tracefd_source_deserialize; - iface->get_is_ready = sysprof_tracefd_source_get_is_ready; - iface->modify_spawn = sysprof_tracefd_source_modify_spawn; - iface->prepare = sysprof_tracefd_source_prepare; - iface->serialize = sysprof_tracefd_source_serialize; - iface->set_writer = sysprof_tracefd_source_set_writer; - iface->stop = sysprof_tracefd_source_stop; -} - -SysprofSource * -sysprof_tracefd_source_new (void) -{ - return g_object_new (SYSPROF_TYPE_TRACEFD_SOURCE, NULL); -} diff --git a/src/libsysprof/sysprof-tracefd-source.h b/src/libsysprof/sysprof-tracefd-source.h deleted file mode 100644 index 1dfd5519..00000000 --- a/src/libsysprof/sysprof-tracefd-source.h +++ /dev/null @@ -1,49 +0,0 @@ -/* sysprof-tracefd-source.h - * - * Copyright 2019 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 - */ - -#pragma once - -#include "sysprof-source.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_TRACEFD_SOURCE (sysprof_tracefd_source_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_DERIVABLE_TYPE (SysprofTracefdSource, sysprof_tracefd_source, SYSPROF, TRACEFD_SOURCE, GObject) - -struct _SysprofTracefdSourceClass -{ - GObjectClass parent_class; - - /*< private >*/ - gpointer _reserved[16]; -}; - -SYSPROF_AVAILABLE_IN_ALL -SysprofSource *sysprof_tracefd_source_new (void); -SYSPROF_AVAILABLE_IN_ALL -const gchar *sysprof_tracefd_source_get_envvar (SysprofTracefdSource *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_tracefd_source_set_envvar (SysprofTracefdSource *self, - const gchar *envvar); - - -G_END_DECLS diff --git a/src/libsysprof/sysprof.h b/src/libsysprof/sysprof.h deleted file mode 100644 index 5abe64b6..00000000 --- a/src/libsysprof/sysprof.h +++ /dev/null @@ -1,67 +0,0 @@ -/* sysprof.h - * - * Copyright 2016-2019 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 . - */ - -#pragma once - -#include -#include - -G_BEGIN_DECLS - -#define SYSPROF_INSIDE - -# include "sysprof-battery-source.h" -# include "sysprof-callgraph-profile.h" -# include "sysprof-capture-autocleanups.h" -# include "sysprof-capture-gobject.h" -# include "sysprof-capture-symbol-resolver.h" -# include "sysprof-control-source.h" -# include "sysprof-diskstat-source.h" -# include "sysprof-elf-symbol-resolver.h" -# include "sysprof-gjs-source.h" -# include "sysprof-governor-source.h" -# include "sysprof-jitmap-symbol-resolver.h" -# include "sysprof-kernel-symbol-resolver.h" -# include "sysprof-kernel-symbol.h" -# include "sysprof-local-profiler.h" -# include "sysprof-memprof-profile.h" -# include "sysprof-memprof-source.h" -# include "sysprof-netdev-source.h" -# include "sysprof-preload-source.h" -# include "sysprof-process-model-item.h" -# include "sysprof-process-model.h" -# include "sysprof-profile.h" -# include "sysprof-profiler.h" -# include "sysprof-proxy-source.h" -# include "sysprof-selection.h" -# include "sysprof-source.h" -# include "sysprof-spawnable.h" -# include "sysprof-symbol-resolver.h" -# include "sysprof-symbols-source.h" -# include "sysprof-tracefd-source.h" - -#ifdef __linux__ -# include "sysprof-hostinfo-source.h" -# include "sysprof-memory-source.h" -# include "sysprof-perf-source.h" -# include "sysprof-proc-source.h" -#endif - -#undef SYSPROF_INSIDE - -G_END_DECLS diff --git a/src/org.gnome.Sysprof.Agent.xml b/src/sysprof-agent/org.gnome.Sysprof.Agent.xml similarity index 100% rename from src/org.gnome.Sysprof.Agent.xml rename to src/sysprof-agent/org.gnome.Sysprof.Agent.xml