From c8e09327fc218774a10e3d0f8e4b2783c60306ef Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 20 Jul 2023 14:30:34 -0700 Subject: [PATCH] libsysprof: add simple text format for category rules --- src/libsysprof/categories.txt | 64 ++++ src/libsysprof/libsysprof.gresource.xml | 6 + src/libsysprof/meson.build | 7 + src/libsysprof/sysprof-callgraph-categorize.c | 130 +------- src/libsysprof/sysprof-categories-private.h | 35 +++ src/libsysprof/sysprof-categories.c | 279 ++++++++++++++++++ 6 files changed, 400 insertions(+), 121 deletions(-) create mode 100644 src/libsysprof/categories.txt create mode 100644 src/libsysprof/libsysprof.gresource.xml create mode 100644 src/libsysprof/sysprof-categories-private.h create mode 100644 src/libsysprof/sysprof-categories.c diff --git a/src/libsysprof/categories.txt b/src/libsysprof/categories.txt new file mode 100644 index 00000000..e98e049a --- /dev/null +++ b/src/libsysprof/categories.txt @@ -0,0 +1,64 @@ +EGL: +egl* paint inherit + + +FontConfig: +* layout inherit + + +GL: +gl* paint inherit + + +GLib: +gtk_main_* main-loop +gtk_wakeup_* main-loop inherit + + +GTK 4: +gtk_widget_snapshot paint inherit +gtk_widget_real_snapshot paint inherit +gtk_widget_render paint inherit +gdk_snapshot_* paint inherit +gtk_snapshot_* paint inherit +gsk_* paint inherit +gdk_frame_clock_paint paint inherit +_gdk_frame_clock_emit_paint paint inherit +_gdk_frame_clock_emit_after_paint paint inherit + +gtk_widget_measure layout inherit +gtk_css_* layout inherit +_gdk_frame_clock_emit_layout layout inherit +gtk_builder_* layout inherit +gtk_buildable_* layout inherit + +gdk_surface_handle_event input inherit +gtk_im_* input inherit +do_update_im_spot_location input inherit + +gtk_window_present* windowing inherit + +gtk_accessible_* a11y inherit +gtk_at_* a11y inherit + + +Harfbuzz: +* layout inherit + + +libc: +poll main-loop inherit +select main-loop inherit +epoll main-loop inherit + + +Pango: +* layout inherit + + +Wayland Client: +wl_* windowing inherit + + +Wayland Server: +wl_* windowing inherit diff --git a/src/libsysprof/libsysprof.gresource.xml b/src/libsysprof/libsysprof.gresource.xml new file mode 100644 index 00000000..bdd76a46 --- /dev/null +++ b/src/libsysprof/libsysprof.gresource.xml @@ -0,0 +1,6 @@ + + + + categories.txt + + diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build index 8c42c13f..0b55302b 100644 --- a/src/libsysprof/meson.build +++ b/src/libsysprof/meson.build @@ -5,6 +5,7 @@ libsysprof_public_sources = [ 'sysprof-callgraph-symbol.c', 'sysprof-callgraph.c', 'sysprof-callgraph-categorize.c', + 'sysprof-categories.c', 'sysprof-cpu-info.c', 'sysprof-cpu-usage.c', 'sysprof-diagnostic.c', @@ -157,6 +158,11 @@ libsysprof_enums = gnome.mkenums_simple('sysprof-enums', install_dir: sysprof_header_dir, ) +libsysprof_resources = gnome.compile_resources('libsysprof-resources', 'libsysprof.gresource.xml', + source_dir: 'resources', + c_name: 'libsysprof', +) + libsysprof_deps = [ dependency('gio-2.0', version: glib_req_version), dependency('gio-unix-2.0', @@ -179,6 +185,7 @@ libsysprof_static = static_library( libsysprof_public_sources + libsysprof_private_sources + libsysprof_enums, + libsysprof_resources, include_directories: [include_directories('.'), libsysprof_capture_include_dirs], diff --git a/src/libsysprof/sysprof-callgraph-categorize.c b/src/libsysprof/sysprof-callgraph-categorize.c index ed79b86e..207a9815 100644 --- a/src/libsysprof/sysprof-callgraph-categorize.c +++ b/src/libsysprof/sysprof-callgraph-categorize.c @@ -21,114 +21,16 @@ #include "config.h" #include "sysprof-callgraph-private.h" +#include "sysprof-categories-private.h" #include "sysprof-symbol-private.h" -enum { - RULE_PREFIX = 1, - RULE_EXACT, -}; - -typedef struct _Rule -{ - guint8 kind : 2; - SysprofCallgraphCategory category; - const char *match; -} Rule; - -typedef struct _RuleGroup -{ - const char *nick; - const Rule *rules; -} RuleGroup; - -static RuleGroup rule_groups[] = { - { "EGL", - (const Rule[]) { - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_PAINT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "egl" }, - { 0 } - } - }, - - { "FontConfig", - (const Rule[]) { - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_LAYOUT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "" }, - { 0 } - } - }, - - { "GLib", - (const Rule[]) { - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_MAIN_LOOP, "g_main_loop_" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_MAIN_LOOP, "g_main_context_" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_MAIN_LOOP|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "g_wakeup_" }, - { RULE_EXACT, SYSPROF_CALLGRAPH_CATEGORY_MAIN_LOOP, "g_main_dispatch" }, - { 0 } - } - }, - - { "GTK 4", - (const Rule[]) { - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_LAYOUT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gtk_css_" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_LAYOUT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gtk_widget_measure" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_PAINT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gdk_snapshot" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_PAINT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gtk_snapshot" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_LAYOUT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gtk_widget_reposition" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_WINDOWING|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gtk_window_present" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_ACTIONS|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gtk_action_muxer_" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_A11Y|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gtk_accessible_" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_A11Y|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gtk_at_" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_LAYOUT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gtk_builder_" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_LAYOUT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gtk_buildable_" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_LAYOUT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gtk_widget_root" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_PAINT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gdk_frame_clock_paint" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_LAYOUT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "_gdk_frame_clock_emit_layout" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_PAINT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "_gdk_frame_clock_emit_paint" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_PAINT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "_gdk_frame_clock_emit_after_paint" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_INPUT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gdk_surface_handle_event" }, - { RULE_EXACT, SYSPROF_CALLGRAPH_CATEGORY_PAINT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gdk_surface_paint_on_clock" }, - { RULE_EXACT, SYSPROF_CALLGRAPH_CATEGORY_PAINT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gdk_widget_render" }, - { RULE_EXACT, SYSPROF_CALLGRAPH_CATEGORY_PAINT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gdk_widget_real_snapshot" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_PAINT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gsk_gl_" }, - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_INPUT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "gtk_im_" }, - { RULE_EXACT, SYSPROF_CALLGRAPH_CATEGORY_INPUT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "do_update_im_spot_location" }, - { 0 } - } - }, - - { "libc", - (const Rule[]) { - { RULE_EXACT, SYSPROF_CALLGRAPH_CATEGORY_MAIN_LOOP|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "poll" }, - { 0 } - } - }, - - { "Pango", - (const Rule[]) { - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_LAYOUT|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "" }, - { 0 } - } - }, - - { "Wayland Client", - (const Rule[]) { - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_WINDOWING|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "wl_" }, - { 0 } - } - }, - - { "Wayland Server", - (const Rule[]) { - { RULE_PREFIX, SYSPROF_CALLGRAPH_CATEGORY_WINDOWING|SYSPROF_CALLGRAPH_CATEGORY_INHERIT, "wl_" }, - { 0 } - } - }, -}; - +static SysprofCategories *categories; SysprofCallgraphCategory _sysprof_callgraph_node_categorize (SysprofCallgraphNode *node) { SysprofSymbol *symbol; + SysprofCallgraphCategory category; g_return_val_if_fail (node, SYSPROF_CALLGRAPH_CATEGORY_UNCATEGORIZED); g_return_val_if_fail (node->summary, SYSPROF_CALLGRAPH_CATEGORY_UNCATEGORIZED); @@ -145,27 +47,13 @@ _sysprof_callgraph_node_categorize (SysprofCallgraphNode *node) symbol->binary_nick == NULL) return SYSPROF_CALLGRAPH_CATEGORY_UNCATEGORIZED; - for (guint i = 0; i < G_N_ELEMENTS (rule_groups); i++) - { - if (strcmp (rule_groups[i].nick, symbol->binary_nick) != 0) - continue; + if G_UNLIKELY (categories == NULL) + categories = sysprof_categories_new (); - for (guint j = 0; rule_groups[i].rules[j].kind; j++) - { - if (rule_groups[i].rules[j].kind == RULE_PREFIX) - { - if (g_str_has_prefix (symbol->name, rule_groups[i].rules[j].match)) - return rule_groups[i].rules[j].category; - } - else if (rule_groups[i].rules[j].kind == RULE_EXACT) - { - if (strcmp (symbol->name, rule_groups[i].rules[j].match) == 0) - return rule_groups[i].rules[j].category; - } - } + category = sysprof_categories_lookup (categories, symbol->binary_nick, symbol->name); - break; - } + if (category == 0) + return SYSPROF_CALLGRAPH_CATEGORY_UNCATEGORIZED; - return SYSPROF_CALLGRAPH_CATEGORY_UNCATEGORIZED; + return category; } diff --git a/src/libsysprof/sysprof-categories-private.h b/src/libsysprof/sysprof-categories-private.h new file mode 100644 index 00000000..c1508bf8 --- /dev/null +++ b/src/libsysprof/sysprof-categories-private.h @@ -0,0 +1,35 @@ +/* sysprof-categories-private.h + * + * Copyright 2023 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-callgraph.h" + +G_BEGIN_DECLS + +typedef struct _SysprofCategories SysprofCategories; + +SysprofCategories *sysprof_categories_new (void); +void sysprof_categories_free (SysprofCategories *categories); +SysprofCallgraphCategory sysprof_categories_lookup (SysprofCategories *categories, + const char *binary_nick, + const char *symbol); + +G_END_DECLS diff --git a/src/libsysprof/sysprof-categories.c b/src/libsysprof/sysprof-categories.c new file mode 100644 index 00000000..8f597105 --- /dev/null +++ b/src/libsysprof/sysprof-categories.c @@ -0,0 +1,279 @@ +/* sysprof-categories.c + * + * Copyright 2023 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 "libsysprof-resources.h" + +#include "sysprof-callgraph-private.h" +#include "sysprof-categories-private.h" +#include "sysprof-enums.h" + +#include "line-reader-private.h" + +struct _SysprofCategories +{ + GHashTable *binary_nick_to_rules; +}; + +enum { + MATCH_EXACT, + MATCH_PREFIX, + MATCH_SUFFIX, +}; + +typedef struct _Rule +{ + guint8 kind : 2; + guint8 inherit : 1; + guint8 category : 5; + char *match; +} Rule; + +static SysprofCallgraphCategory +parse_category (const char *category) +{ + static GEnumClass *enum_class; + const GEnumValue *value; + + if (enum_class == NULL) + enum_class = g_type_class_ref (SYSPROF_TYPE_CALLGRAPH_CATEGORY); + + if (!(value = g_enum_get_value_by_nick (enum_class, category))) + return 0; + + return value->value; +} + +static void +clear_rule (gpointer element) +{ + Rule *rule = element; + + g_clear_pointer (&rule->match, g_free); +} + +static void +sysprof_categories_add_rule (SysprofCategories *categories, + const char *binary_nick, + const char *match, + SysprofCallgraphCategory category, + gboolean inherit) +{ + Rule rule; + GArray *ar; + + g_assert (categories != NULL); + g_assert (binary_nick != NULL); + g_assert (match != NULL); + + if (match[0] == 0) + return; + + if G_UNLIKELY (!(ar = g_hash_table_lookup (categories->binary_nick_to_rules, binary_nick))) + { + ar = g_array_new (FALSE, FALSE, sizeof (Rule)); + g_array_set_clear_func (ar, clear_rule); + g_hash_table_insert (categories->binary_nick_to_rules, + g_strdup (binary_nick), + ar); + } + + if (match[0] == '*') + { + rule.kind = MATCH_SUFFIX; + rule.match = g_strdup (&match[1]); + rule.category = category; + rule.inherit = inherit; + + g_array_append_val (ar, rule); + } + else if (match[strlen(match)-1] == '*') + { + rule.kind = MATCH_PREFIX; + rule.match = g_strndup (match, strlen (match)-1); + rule.category = category; + rule.inherit = inherit; + + g_array_append_val (ar, rule); + } + else + { + rule.kind = MATCH_EXACT; + rule.match = g_strdup (match); + rule.category = category; + rule.inherit = inherit; + + g_array_append_val (ar, rule); + } +} + +SysprofCategories * +sysprof_categories_new (void) +{ + SysprofCategories *categories; + g_autoptr(GBytes) bytes = NULL; + g_autofree char *binary_nick = NULL; + const char *str; + const char *lineptr; + gsize line_len; + gsize len; + guint lineno = 0; + LineReader reader; + + g_resources_register (libsysprof_get_resource ()); + + if (!(bytes = g_resources_lookup_data ("/org/gnome/libsysprof/categories.txt", 0, NULL))) + return NULL; + + if (!(str = (const char *)g_bytes_get_data (bytes, &len))) + return NULL; + + line_reader_init (&reader, (char *)str, len); + + categories = g_new0 (SysprofCategories, 1); + categories->binary_nick_to_rules = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify)g_array_unref); + + while ((lineptr = line_reader_next (&reader, &line_len))) + { + g_autofree char *line = g_strndup (lineptr, line_len); + SysprofCallgraphCategory category_value; + g_auto(GStrv) parts = NULL; + const char *match = NULL; + const char *category = NULL; + const char *inherit = NULL; + + lineno++; + g_strstrip (line); + + if (line[0] == 0) + continue; + + /* Group lines look like "binary nick:\n" */ + if (g_str_has_suffix (line, ":")) + { + line[line_len-1] = 0; + g_set_str (&binary_nick, g_strstrip (line)); + continue; + } + + parts = g_strsplit (line, " ", 0); + + for (guint i = 0; parts[i]; i++) + { + g_strstrip (parts[i]); + + if (parts[i][0] == 0) + continue; + + if (match == NULL) + { + match = parts[i]; + continue; + } + + if (category == NULL) + { + category = parts[i]; + continue; + } + + if (inherit == NULL) + { + inherit = parts[i]; + continue; + } + + break; + } + + if (match == NULL || category == NULL) + { + g_warning ("categories.txt: line: %d: Incomplete rule", lineno); + continue; + } + + if (inherit && !g_str_equal (inherit, "inherit")) + { + g_warning ("categories.txt line %d: malformated inherit", lineno); + continue; + } + + if (!(category_value = parse_category (category))) + { + g_warning ("categories.txt line %d: malformated category", lineno); + continue; + } + + sysprof_categories_add_rule (categories, binary_nick, match, category_value, !!inherit); + } + + return categories; +} + +void +sysprof_categories_free (SysprofCategories *categories) +{ + if (categories != NULL) + { + g_clear_pointer (&categories->binary_nick_to_rules, g_hash_table_unref); + g_free (categories); + } +} + +SysprofCallgraphCategory +sysprof_categories_lookup (SysprofCategories *categories, + const char *binary_nick, + const char *symbol) +{ + GArray *rules; + + if (binary_nick == NULL || symbol == NULL) + return 0; + + if (!(rules = g_hash_table_lookup (categories->binary_nick_to_rules, binary_nick))) + return 0; + + for (guint i = 0; i < rules->len; i++) + { + const Rule *rule = &g_array_index (rules, Rule, i); + gboolean ret = FALSE; + + if (rule->kind == MATCH_EXACT) + ret = strcmp (rule->match, symbol) == 0; + else if (rule->kind == MATCH_PREFIX) + ret = g_str_has_prefix (symbol, rule->match); + else if (rule->kind == MATCH_SUFFIX) + ret = g_str_has_suffix (symbol, rule->match); + + if (ret) + { + if (rule->inherit) + return rule->category | SYSPROF_CALLGRAPH_CATEGORY_INHERIT; + + return rule->category; + } + } + + return 0; +}