AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/07 02:26 flow patch-triage
1m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch series modifies only user-space tooling and build scripts under tools/ (specifically porting the perf GTK UI from GTK2 to GTK4 and minor helper relocation in tools/perf/util/annotate.c). It does not alter any kernel-space code, the compiled kernel image (vmlinux), or kernel modules.",
  "WorthFuzzing": false
}

1/1 2026/09/07 02:26 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 5faf41536400fc72093df78d6e797a37cbab06f7\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Sep 7 02:26:22 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature\nindex 99eb0ea095375..4ec95c35a5c19 100644\n--- a/tools/build/Makefile.feature\n+++ b/tools/build/Makefile.feature\n@@ -113,8 +113,7 @@ FEATURE_TESTS_EXTRA :=                  \\\n          compile-x32                    \\\n          cplus-demangle                 \\\n          cxa-demangle                   \\\n-         gtk2                           \\\n-         gtk2-infobar                   \\\n+         gtk4                           \\\n          hello                          \\\n          babeltrace2-ctf-writer         \\\n          libcapstone                    \\\n@@ -143,6 +142,7 @@ endif\n FEATURE_DISPLAY ?=              \\\n          libdw                  \\\n          glibc                  \\\n+         gtk4                   \\\n          libelf                 \\\n          libnuma                \\\n          numa_num_possible_cpus \\\ndiff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile\nindex 7d165018116a5..01c48e6ef021d 100644\n--- a/tools/build/feature/Makefile\n+++ b/tools/build/feature/Makefile\n@@ -11,8 +11,7 @@ FILES=                                          \\\n          test-eventfd.bin                       \\\n          test-fortify-source.bin                \\\n          test-glibc.bin                         \\\n-         test-gtk2.bin                          \\\n-         test-gtk2-infobar.bin                  \\\n+         test-gtk4.bin                          \\\n          test-hello.bin                         \\\n          test-libbfd.bin                        \\\n \t test-libbfd-threadsafe.bin      \t\\\n@@ -240,11 +239,8 @@ $(OUTPUT)test-libcpupower.bin:\n $(OUTPUT)test-libtracefs.bin:\n \t $(BUILD) $(shell $(PKG_CONFIG) --cflags libtracefs 2\u003e/dev/null) -ltracefs\n \n-$(OUTPUT)test-gtk2.bin:\n-\t$(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2\u003e/dev/null) -Wno-deprecated-declarations\n-\n-$(OUTPUT)test-gtk2-infobar.bin:\n-\t$(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2\u003e/dev/null)\n+$(OUTPUT)test-gtk4.bin:\n+\t$(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk4 2\u003e/dev/null)\n \n grep-libs  = $(filter -l%,$(1))\n strip-libs = $(filter-out -l%,$(1))\ndiff --git a/tools/build/feature/test-gtk2-infobar.c b/tools/build/feature/test-gtk2-infobar.c\ndeleted file mode 100644\nindex b1b716dd57330..0000000000000\n--- a/tools/build/feature/test-gtk2-infobar.c\n+++ /dev/null\n@@ -1,12 +0,0 @@\n-// SPDX-License-Identifier: GPL-2.0\n-#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"\n-#include \u003cgtk/gtk.h\u003e\n-#pragma GCC diagnostic error \"-Wstrict-prototypes\"\n-\n-int main(int argc, char *argv[])\n-{\n-\tgtk_init(\u0026argc, \u0026argv);\n-\tgtk_info_bar_new();\n-\n-\treturn 0;\n-}\ndiff --git a/tools/build/feature/test-gtk2.c b/tools/build/feature/test-gtk4.c\nsimilarity index 76%\nrename from tools/build/feature/test-gtk2.c\nrename to tools/build/feature/test-gtk4.c\nindex 2aaf4bfc20556..b9520e7408b99 100644\n--- a/tools/build/feature/test-gtk2.c\n+++ b/tools/build/feature/test-gtk4.c\n@@ -3,9 +3,9 @@\n #include \u003cgtk/gtk.h\u003e\n #pragma GCC diagnostic error \"-Wstrict-prototypes\"\n \n-int main(int argc, char *argv[])\n+int main(void)\n {\n-\tgtk_init(\u0026argc, \u0026argv);\n+\tgtk_init();\n \n         return 0;\n }\ndiff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt\nindex 22f87eaa32796..7af9b3f81c065 100644\n--- a/tools/perf/Documentation/perf-report.txt\n+++ b/tools/perf/Documentation/perf-report.txt\n@@ -351,7 +351,7 @@ OPTIONS\n \trequires a tty, if one is not present, as when piping to other\n \tcommands, the stdio interface is used.\n \n---gtk:: Use the GTK2 interface.\n+--gtk:: Use the GTK4 interface.\n \n -k::\n --vmlinux=\u003cfile\u003e::\ndiff --git a/tools/perf/Makefile b/tools/perf/Makefile\nindex 5b713837eede7..56014106479a3 100644\n--- a/tools/perf/Makefile\n+++ b/tools/perf/Makefile\n@@ -106,7 +106,7 @@ clean:\n # make -C tools/perf -f tests/make\n #\n build-test:\n-\t@$(MAKE) SHUF=1 -f tests/make REUSE_FEATURES_DUMP=1 MK=Makefile SET_PARALLEL=1 --no-print-directory tarpkg make_static make_with_gtk2 out\n+\t@$(MAKE) SHUF=1 -f tests/make REUSE_FEATURES_DUMP=1 MK=Makefile SET_PARALLEL=1 --no-print-directory tarpkg make_static make_with_gtk4 out\n \n build-test-tarball:\n \t@$(MAKE) -f tests/make REUSE_FEATURES_DUMP=1 MK=Makefile SET_PARALLEL=1 --no-print-directory out\ndiff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config\nindex 4d5993da9f945..4ee7393a39f91 100644\n--- a/tools/perf/Makefile.config\n+++ b/tools/perf/Makefile.config\n@@ -774,21 +774,22 @@ ifndef NO_SLANG\n   endif\n endif\n \n-ifdef GTK2\n-  FLAGS_GTK2=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2\u003e/dev/null)\n-  $(call feature_check,gtk2)\n-  ifneq ($(feature-gtk2), 1)\n-    $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev)\n-    NO_GTK2 := 1\n+ifdef GTK4\n+  FLAGS_GTK4=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) $(shell $(PKG_CONFIG) --libs --cflags gtk4 2\u003e/dev/null)\n+  $(call feature_check,gtk4)\n+  ifneq ($(feature-gtk4), 1)\n+    $(warning GTK4 not found, disables GTK4 support. Please install gtk4-devel or libgtk-4-dev)\n+    NO_GTK4 := 1\n   else\n-    $(call feature_check,gtk2-infobar)\n-    ifeq ($(feature-gtk2-infobar), 1)\n-      GTK_CFLAGS := -DHAVE_GTK_INFO_BAR_SUPPORT\n-    endif\n-    CFLAGS += -DHAVE_GTK2_SUPPORT\n-    GTK_CFLAGS += $(shell $(PKG_CONFIG) --cflags gtk+-2.0 2\u003e/dev/null)\n-    GTK_LIBS := $(shell $(PKG_CONFIG) --libs gtk+-2.0 2\u003e/dev/null)\n+    CFLAGS += -DHAVE_GTK4_SUPPORT\n+    GTK_CFLAGS += $(shell $(PKG_CONFIG) --cflags gtk4 2\u003e/dev/null)\n+    GTK_LIBS := $(shell $(PKG_CONFIG) --libs gtk4 2\u003e/dev/null)\n     EXTLIBS += -ldl\n+    # libperf-gtk.so is dlopen()ed at runtime and calls back into\n+    # symbols defined in the perf binary itself (callchain_param,\n+    # symbol_conf, evsel__name, ...): perf needs to export those\n+    # dynamically for the plugin to resolve them.\n+    LDFLAGS += -rdynamic\n   endif\n endif\n \ndiff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf\nindex 29cfd44c427f3..2438b40eaaec1 100644\n--- a/tools/perf/Makefile.perf\n+++ b/tools/perf/Makefile.perf\n@@ -49,7 +49,7 @@ include ../scripts/utilities.mak\n #\n # Define NO_SLANG if you do not want TUI support.\n #\n-# Define GTK2 if you want GTK+ GUI support.\n+# Define GTK4 if you want GTK+ GUI support.\n #\n # Define NO_DEMANGLE if you do not want C++ symbol demangling.\n #\n@@ -473,7 +473,7 @@ ifneq ($(OUTPUT),)\n   CFLAGS += -I$(OUTPUT)\n endif\n \n-ifdef GTK2\n+ifdef GTK4\n   ALL_PROGRAMS += $(OUTPUT)libperf-gtk.so\n   GTK_IN := $(OUTPUT)gtk-in.o\n endif\n@@ -811,7 +811,7 @@ check: prepare\n \n ### Installation rules\n \n-ifdef GTK2\n+ifdef GTK4\n install-gtk: $(OUTPUT)libperf-gtk.so\n \t$(call QUIET_INSTALL, 'GTK UI') \\\n \t\t$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(libdir_SQ)'; \\\ndiff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c\nindex 69cb72b2082a6..15163e081a8ce 100644\n--- a/tools/perf/builtin-annotate.c\n+++ b/tools/perf/builtin-annotate.c\n@@ -52,7 +52,7 @@ struct perf_annotate {\n \tbool\t   use_tui;\n #endif\n \tbool\t   use_stdio, use_stdio2;\n-#ifdef HAVE_GTK2_SUPPORT\n+#ifdef HAVE_GTK4_SUPPORT\n \tbool\t   use_gtk;\n #endif\n \tbool\t   skip_missing;\n@@ -712,7 +712,7 @@ int cmd_annotate(int argc, const char **argv)\n \tOPT_BOOLEAN('q', \"quiet\", \u0026quiet, \"do now show any warnings or messages\"),\n \tOPT_BOOLEAN('D', \"dump-raw-trace\", \u0026dump_trace,\n \t\t    \"dump raw trace in ASCII\"),\n-#ifdef HAVE_GTK2_SUPPORT\n+#ifdef HAVE_GTK4_SUPPORT\n \tOPT_BOOLEAN(0, \"gtk\", \u0026annotate.use_gtk, \"Use the GTK interface\"),\n #endif\n #ifdef HAVE_SLANG_SUPPORT\n@@ -828,7 +828,7 @@ int cmd_annotate(int argc, const char **argv)\n \tif (annotate_check_args() \u003c 0)\n \t\treturn -EINVAL;\n \n-#ifdef HAVE_GTK2_SUPPORT\n+#ifdef HAVE_GTK4_SUPPORT\n \tif (symbol_conf.show_nr_samples \u0026\u0026 annotate.use_gtk) {\n \t\tpr_err(\"--show-nr-samples is not available in --gtk mode at this time\\n\");\n \t\treturn ret;\n@@ -898,7 +898,7 @@ int cmd_annotate(int argc, const char **argv)\n \telse if (annotate.use_tui)\n \t\tuse_browser = 1;\n #endif\n-#ifdef HAVE_GTK2_SUPPORT\n+#ifdef HAVE_GTK4_SUPPORT\n \telse if (annotate.use_gtk)\n \t\tuse_browser = 2;\n #endif\ndiff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c\nindex 60d1f166629e0..d14384c584661 100644\n--- a/tools/perf/builtin-report.c\n+++ b/tools/perf/builtin-report.c\n@@ -82,7 +82,7 @@ struct report {\n #ifdef HAVE_SLANG_SUPPORT\n \tbool\t\t\tuse_tui;\n #endif\n-#ifdef HAVE_GTK2_SUPPORT\n+#ifdef HAVE_GTK4_SUPPORT\n \tbool\t\t\tuse_gtk;\n #endif\n \tbool\t\t\tuse_stdio;\n@@ -1359,8 +1359,8 @@ int cmd_report(int argc, const char **argv)\n #ifdef HAVE_SLANG_SUPPORT\n \tOPT_BOOLEAN(0, \"tui\", \u0026report.use_tui, \"Use the TUI interface\"),\n #endif\n-#ifdef HAVE_GTK2_SUPPORT\n-\tOPT_BOOLEAN(0, \"gtk\", \u0026report.use_gtk, \"Use the GTK2 interface\"),\n+#ifdef HAVE_GTK4_SUPPORT\n+\tOPT_BOOLEAN(0, \"gtk\", \u0026report.use_gtk, \"Use the GTK4 interface\"),\n #endif\n \tOPT_BOOLEAN(0, \"stdio\", \u0026report.use_stdio,\n \t\t    \"Use the stdio interface\"),\n@@ -1710,7 +1710,7 @@ int cmd_report(int argc, const char **argv)\n \telse if (report.use_tui)\n \t\tuse_browser = 1;\n #endif\n-#ifdef HAVE_GTK2_SUPPORT\n+#ifdef HAVE_GTK4_SUPPORT\n \telse if (report.use_gtk)\n \t\tuse_browser = 2;\n #endif\ndiff --git a/tools/perf/scripts/install-build-deps.sh b/tools/perf/scripts/install-build-deps.sh\nindex d003e7fab2be8..a601a5260c17e 100755\n--- a/tools/perf/scripts/install-build-deps.sh\n+++ b/tools/perf/scripts/install-build-deps.sh\n@@ -199,8 +199,8 @@ fedora_pkg_for() {\n \t# opt-in features, which a default build does not enable: the libbfd\n \t# disassembler family (libbfd, libbfd-threadsafe, libbfd-liberty,\n \t# disassembler-*, cplus-demangle), only linked on BUILD_NONDISTRO\n-\t# builds and deprecated in favor of capstone, GTK2, LIBPERL and\n-\t# LIBUNWIND support (ifdef GTK2 / ifdef LIBPERL / LIBUNWIND=1),\n+\t# builds and deprecated in favor of capstone, GTK4, LIBPERL and\n+\t# LIBUNWIND support (ifdef GTK4 / ifdef LIBPERL / LIBUNWIND=1),\n \t# and CoreSight (ifdef CORESIGHT), are deliberately not mapped.\n \t# libaio is not mapped either: its\n \t# test uses the POSIX AIO API (aio.h, aio_*, -lrt), provided by\ndiff --git a/tools/perf/tests/make b/tools/perf/tests/make\nindex d2c2f526e1db0..202ab55019161 100644\n--- a/tools/perf/tests/make\n+++ b/tools/perf/tests/make\n@@ -95,7 +95,7 @@ make_no_babeltrace2 := NO_BABELTRACE2=1\n make_with_coresight := CORESIGHT=1\n make_no_sdt\t    := NO_SDT=1\n make_no_libpfm4     := NO_LIBPFM4=1\n-make_with_gtk2      := GTK2=1\n+make_with_gtk4      := GTK4=1\n make_refcnt_check   := EXTRA_CFLAGS=\"-DREFCNT_CHECKING=1\"\n make_tags           := tags\n make_cscope         := cscope\n@@ -318,7 +318,7 @@ $(run):\n \t$(call test,$@) \u0026\u0026 \\\n \trm -rf $@ $$TMP_DEST || (cat $@ ; false)\n \n-make_with_gtk2:\n+make_with_gtk4:\n \t$(call clean)\n \t@TMP_DEST=$$(mktemp -d); \\\n \tcmd=\"cd $(PERF) \u0026\u0026 $(MAKE_F) $($@) $(PARALLEL_OPT) $(O_OPT) DESTDIR=$$TMP_DEST\"; \\\ndiff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c\nindex 8920e298420a8..094dfa045b774 100644\n--- a/tools/perf/ui/gtk/annotate.c\n+++ b/tools/perf/ui/gtk/annotate.c\n@@ -11,6 +11,7 @@\n #include \"ui/helpline.h\"\n #include \u003cinttypes.h\u003e\n #include \u003csignal.h\u003e\n+#include \u003cstring.h\u003e\n \n enum {\n \tANN_COL__PERCENT,\n@@ -161,7 +162,7 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct map_symbol *ms,\n \t\t\tgtk_list_store_set(store, \u0026iter, ANN_COL__LINE, s, -1);\n \t}\n \n-\tgtk_container_add(GTK_CONTAINER(window), view);\n+\tgtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(window), view);\n \n \tlist_for_each_entry_safe(pos, n, \u0026notes-\u003esrc-\u003esource, al.node) {\n \t\tlist_del_init(\u0026pos-\u003eal.node);\n@@ -205,40 +206,34 @@ static int symbol__gtk_annotate(struct map_symbol *ms, struct evsel *evsel,\n \t\tGtkWidget *infobar;\n \t\tGtkWidget *statbar;\n \n-\t\tsignal(SIGSEGV, perf_gtk__signal);\n-\t\tsignal(SIGFPE,  perf_gtk__signal);\n-\t\tsignal(SIGINT,  perf_gtk__signal);\n-\t\tsignal(SIGQUIT, perf_gtk__signal);\n-\t\tsignal(SIGTERM, perf_gtk__signal);\n+\t\tsignal(SIGSEGV, perf_gtk__fatal_signal);\n+\t\tsignal(SIGFPE,  perf_gtk__fatal_signal);\n+\t\tperf_gtk__install_quit_signals();\n \n-\t\twindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);\n+\t\twindow = gtk_window_new();\n \t\tgtk_window_set_title(GTK_WINDOW(window), \"perf annotate\");\n \n-\t\tg_signal_connect(window, \"delete_event\", gtk_main_quit, NULL);\n-\n \t\tpgctx = perf_gtk__activate_context(window);\n \t\tif (!pgctx)\n \t\t\treturn -1;\n \n-\t\tvbox = gtk_vbox_new(FALSE, 0);\n+\t\tvbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);\n \t\tnotebook = gtk_notebook_new();\n \t\tpgctx-\u003enotebook = notebook;\n \n-\t\tgtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);\n+\t\tgtk_widget_set_vexpand(notebook, TRUE);\n+\t\tgtk_box_append(GTK_BOX(vbox), notebook);\n \n \t\tinfobar = perf_gtk__setup_info_bar();\n-\t\tif (infobar) {\n-\t\t\tgtk_box_pack_start(GTK_BOX(vbox), infobar,\n-\t\t\t\t\t   FALSE, FALSE, 0);\n-\t\t}\n+\t\tgtk_box_append(GTK_BOX(vbox), infobar);\n \n \t\tstatbar = perf_gtk__setup_statusbar();\n-\t\tgtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);\n+\t\tgtk_box_append(GTK_BOX(vbox), statbar);\n \n-\t\tgtk_container_add(GTK_CONTAINER(window), vbox);\n+\t\tgtk_window_set_child(GTK_WINDOW(window), vbox);\n \t}\n \n-\tscrolled_window = gtk_scrolled_window_new(NULL, NULL);\n+\tscrolled_window = gtk_scrolled_window_new();\n \ttab_label = gtk_label_new(sym-\u003ename);\n \n \tgtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),\n@@ -267,12 +262,11 @@ void perf_gtk__show_annotations(void)\n \t\treturn;\n \n \twindow = pgctx-\u003emain_window;\n-\tgtk_widget_show_all(window);\n \n \tperf_gtk__resize_window(window);\n-\tgtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);\n+\tgtk_widget_set_visible(window, TRUE);\n \n-\tgtk_main();\n+\tperf_gtk__run_main_loop(window);\n \n \tperf_gtk__deactivate_context(\u0026pgctx);\n }\ndiff --git a/tools/perf/ui/gtk/browser.c b/tools/perf/ui/gtk/browser.c\nindex d2dadf3873fbd..98f1d6bfcb246 100644\n--- a/tools/perf/ui/gtk/browser.c\n+++ b/tools/perf/ui/gtk/browser.c\n@@ -5,32 +5,92 @@\n #include \"../hist.h\"\n #include \"../helpline.h\"\n \n+#include \u003cglib-unix.h\u003e\n #include \u003csignal.h\u003e\n \n-void perf_gtk__signal(int sig)\n+/*\n+ * SIGINT/SIGQUIT/SIGTERM are asynchronous: deferring the actual exit to a\n+ * GLib source dispatched from the main loop means it always runs on the\n+ * main thread, serialized with everything else the main loop does\n+ * (including perf_gtk__error()'s updates to perf_gtk__error_loops), instead\n+ * of racing them from arbitrary signal-handler context.\n+ */\n+static gboolean perf_gtk__quit_signal(gpointer data __maybe_unused)\n {\n \tperf_gtk__exit(false);\n+\treturn G_SOURCE_REMOVE;\n+}\n+\n+void perf_gtk__install_quit_signals(void)\n+{\n+\tg_unix_signal_add(SIGINT,  perf_gtk__quit_signal, NULL);\n+\tg_unix_signal_add(SIGQUIT, perf_gtk__quit_signal, NULL);\n+\tg_unix_signal_add(SIGTERM, perf_gtk__quit_signal, NULL);\n+}\n+\n+/*\n+ * SIGSEGV/SIGFPE are synchronous faults: there's no \"later\" to defer to,\n+ * and no safe way to run GTK/GLib code (or anything else non-async-signal-\n+ * safe) from the faulting context. Report and let the default disposition\n+ * (core dump) happen instead of trying to tear down GTK state here.\n+ */\n+void perf_gtk__fatal_signal(int sig)\n+{\n \tpsignal(sig, \"perf\");\n+\tsignal(sig, SIG_DFL);\n+\traise(sig);\n }\n \n void perf_gtk__resize_window(GtkWidget *window)\n {\n \tGdkRectangle rect;\n-\tGdkScreen *screen;\n-\tint monitor;\n+\tGdkMonitor *monitor;\n+\tGdkDisplay *display;\n+\tGListModel *monitors;\n \tint height;\n \tint width;\n \n-\tscreen = gtk_widget_get_screen(window);\n+\tdisplay = gtk_widget_get_display(window);\n+\tmonitors = gdk_display_get_monitors(display);\n+\tmonitor = g_list_model_get_item(monitors, 0);\n+\tif (!monitor) {\n+\t\tgtk_window_set_default_size(GTK_WINDOW(window), 800, 600);\n+\t\treturn;\n+\t}\n \n-\tmonitor = gdk_screen_get_monitor_at_window(screen, window-\u003ewindow);\n-\n-\tgdk_screen_get_monitor_geometry(screen, monitor, \u0026rect);\n+\tgdk_monitor_get_geometry(monitor, \u0026rect);\n+\tg_object_unref(monitor);\n \n \twidth\t= rect.width * 3 / 4;\n \theight\t= rect.height * 3 / 4;\n \n-\tgtk_window_resize(GTK_WINDOW(window), width, height);\n+\tgtk_window_set_default_size(GTK_WINDOW(window), width, height);\n+}\n+\n+static GMainLoop *perf_gtk__main_loop;\n+\n+void perf_gtk__quit_main_loop(void)\n+{\n+\tif (perf_gtk__main_loop)\n+\t\tg_main_loop_quit(perf_gtk__main_loop);\n+}\n+\n+static gboolean perf_gtk__close_request(GtkWidget *widget __maybe_unused,\n+\t\t\t\t\tgpointer data __maybe_unused)\n+{\n+\tperf_gtk__quit_main_loop();\n+\n+\treturn FALSE;\n+}\n+\n+void perf_gtk__run_main_loop(GtkWidget *window)\n+{\n+\tg_signal_connect(window, \"close-request\",\n+\t\t\t G_CALLBACK(perf_gtk__close_request), NULL);\n+\n+\tperf_gtk__main_loop = g_main_loop_new(NULL, FALSE);\n+\tg_main_loop_run(perf_gtk__main_loop);\n+\tg_clear_pointer(\u0026perf_gtk__main_loop, g_main_loop_unref);\n }\n \n const char *perf_gtk__get_percent_color(double percent)\n@@ -42,33 +102,35 @@ const char *perf_gtk__get_percent_color(double percent)\n \treturn NULL;\n }\n \n-#ifdef HAVE_GTK_INFO_BAR_SUPPORT\n+static void perf_gtk__hide_widget(GtkWidget *widget, gint response_id __maybe_unused,\n+\t\t\t\t   gpointer data __maybe_unused)\n+{\n+\tgtk_widget_set_visible(widget, FALSE);\n+}\n+\n GtkWidget *perf_gtk__setup_info_bar(void)\n {\n \tGtkWidget *info_bar;\n \tGtkWidget *label;\n-\tGtkWidget *content_area;\n \n \tinfo_bar = gtk_info_bar_new();\n-\tgtk_widget_set_no_show_all(info_bar, TRUE);\n+\tgtk_widget_set_visible(info_bar, FALSE);\n \n \tlabel = gtk_label_new(\"\");\n-\tgtk_widget_show(label);\n+\tgtk_widget_set_visible(label, TRUE);\n \n-\tcontent_area = gtk_info_bar_get_content_area(GTK_INFO_BAR(info_bar));\n-\tgtk_container_add(GTK_CONTAINER(content_area), label);\n+\tgtk_info_bar_add_child(GTK_INFO_BAR(info_bar), label);\n \n-\tgtk_info_bar_add_button(GTK_INFO_BAR(info_bar), GTK_STOCK_OK,\n+\tgtk_info_bar_add_button(GTK_INFO_BAR(info_bar), \"_OK\",\n \t\t\t\tGTK_RESPONSE_OK);\n \tg_signal_connect(info_bar, \"response\",\n-\t\t\t G_CALLBACK(gtk_widget_hide), NULL);\n+\t\t\t G_CALLBACK(perf_gtk__hide_widget), NULL);\n \n \tpgctx-\u003einfo_bar = info_bar;\n \tpgctx-\u003emessage_label = label;\n \n \treturn info_bar;\n }\n-#endif\n \n GtkWidget *perf_gtk__setup_statusbar(void)\n {\ndiff --git a/tools/perf/ui/gtk/gtk.h b/tools/perf/ui/gtk/gtk.h\nindex a2b497f03fd6e..6b37fd9943c9d 100644\n--- a/tools/perf/ui/gtk/gtk.h\n+++ b/tools/perf/ui/gtk/gtk.h\n@@ -13,10 +13,8 @@ struct perf_gtk_context {\n \tGtkWidget *main_window;\n \tGtkWidget *notebook;\n \n-#ifdef HAVE_GTK_INFO_BAR_SUPPORT\n \tGtkWidget *info_bar;\n \tGtkWidget *message_label;\n-#endif\n \tGtkWidget *statbar;\n \tguint statbar_ctx_id;\n };\n@@ -38,19 +36,15 @@ void perf_gtk__init_helpline(void);\n void gtk_ui_progress__init(void);\n void perf_gtk__init_hpp(void);\n \n-void perf_gtk__signal(int sig);\n+void perf_gtk__install_quit_signals(void);\n+void perf_gtk__fatal_signal(int sig);\n void perf_gtk__resize_window(GtkWidget *window);\n+void perf_gtk__run_main_loop(GtkWidget *window);\n+void perf_gtk__quit_main_loop(void);\n+void perf_gtk__quit_error_dialog(void);\n const char *perf_gtk__get_percent_color(double percent);\n GtkWidget *perf_gtk__setup_statusbar(void);\n-\n-#ifdef HAVE_GTK_INFO_BAR_SUPPORT\n GtkWidget *perf_gtk__setup_info_bar(void);\n-#else\n-static inline GtkWidget *perf_gtk__setup_info_bar(void)\n-{\n-\treturn NULL;\n-}\n-#endif\n \n struct evsel;\n struct evlist;\ndiff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c\nindex bae21f336ae6d..342d4c3fecb01 100644\n--- a/tools/perf/ui/gtk/hists.c\n+++ b/tools/perf/ui/gtk/hists.c\n@@ -8,6 +8,8 @@\n #include \"../helpline.h\"\n #include \"../string2.h\"\n #include \u003csignal.h\u003e\n+#include \u003cstdarg.h\u003e\n+#include \u003cstdio.h\u003e\n #include \u003cstdlib.h\u003e\n #include \u003clinux/string.h\u003e\n \n@@ -395,11 +397,9 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,\n \t\t}\n \t}\n \n-\tgtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);\n-\n \tg_signal_connect(view, \"row-activated\",\n \t\t\t G_CALLBACK(on_row_activated), NULL);\n-\tgtk_container_add(GTK_CONTAINER(window), view);\n+\tgtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(window), view);\n }\n \n static void perf_gtk__add_hierarchy_entries(struct hists *hists,\n@@ -449,7 +449,7 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,\n \t\tbf = hpp-\u003ebuf;\n \t\tsize = hpp-\u003esize;\n \t\tperf_hpp_list__for_each_format(he-\u003ehpp_list, fmt) {\n-\t\t\tint ret;\n+\t\t\tint ret, inc;\n \n \t\t\tif (fmt-\u003ecolor)\n \t\t\t\tret = fmt-\u003ecolor(fmt, hpp, he);\n@@ -457,7 +457,18 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,\n \t\t\t\tret = fmt-\u003eentry(fmt, hpp, he);\n \n \t\t\tsnprintf(hpp-\u003ebuf + ret, hpp-\u003esize - ret, \"  \");\n-\t\t\tadvance_hpp(hpp, ret + 2);\n+\t\t\t/*\n+\t\t\t * ret can be as large as hpp-\u003esize - 1, so ret + 2\n+\t\t\t * can exceed hpp-\u003esize. advance_hpp() doesn't clamp,\n+\t\t\t * so passing that through would underflow the\n+\t\t\t * size_t hpp-\u003esize and let a later fmt-\u003eentry() in\n+\t\t\t * this loop write past the end of the caller's\n+\t\t\t * stack buffer.\n+\t\t\t */\n+\t\t\tinc = ret + 2;\n+\t\t\tif (inc \u003e (int)hpp-\u003esize)\n+\t\t\t\tinc = hpp-\u003esize;\n+\t\t\tadvance_hpp(hpp, inc);\n \t\t}\n \n \t\tgtk_tree_store_set(store, \u0026iter, col_idx, strim(bf), -1);\n@@ -505,6 +516,7 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,\n \tGtkWidget *view;\n \tint col_idx;\n \tint nr_cols = 0;\n+\tint ret;\n \tchar s[512];\n \tchar buf[512];\n \tbool first_node, first_col;\n@@ -541,9 +553,10 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,\n \t/* construct merged column header since sort keys share single column */\n \tbuf[0] = '\\0';\n \tfirst_node = true;\n+\tret = 0;\n \tlist_for_each_entry_continue(fmt_node, \u0026hists-\u003ehpp_formats, list) {\n \t\tif (!first_node)\n-\t\t\tstrcat(buf, \" / \");\n+\t\t\tret += scnprintf(buf + ret, sizeof(buf) - ret, \" / \");\n \t\tfirst_node = false;\n \n \t\tfirst_col = true;\n@@ -552,11 +565,11 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,\n \t\t\t\tcontinue;\n \n \t\t\tif (!first_col)\n-\t\t\t\tstrcat(buf, \"+\");\n+\t\t\t\tret += scnprintf(buf + ret, sizeof(buf) - ret, \"+\");\n \t\t\tfirst_col = false;\n \n \t\t\tfmt-\u003eheader(fmt, \u0026hpp, hists, 0, NULL);\n-\t\t\tstrcat(buf, strim(hpp.buf));\n+\t\t\tret += scnprintf(buf + ret, sizeof(buf) - ret, \"%s\", strim(hpp.buf));\n \t\t}\n \t}\n \n@@ -583,11 +596,9 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,\n \tperf_gtk__add_hierarchy_entries(hists, \u0026hists-\u003eentries, store,\n \t\t\t\t\tNULL, \u0026hpp, min_pcnt);\n \n-\tgtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);\n-\n \tg_signal_connect(view, \"row-activated\",\n \t\t\t G_CALLBACK(on_row_activated), NULL);\n-\tgtk_container_add(GTK_CONTAINER(window), view);\n+\tgtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(window), view);\n }\n \n int evlist__gtk_browse_hists(struct evlist *evlist, const char *help,\n@@ -600,36 +611,32 @@ int evlist__gtk_browse_hists(struct evlist *evlist, const char *help,\n \tGtkWidget *statbar;\n \tGtkWidget *window;\n \n-\tsignal(SIGSEGV, perf_gtk__signal);\n-\tsignal(SIGFPE,  perf_gtk__signal);\n-\tsignal(SIGINT,  perf_gtk__signal);\n-\tsignal(SIGQUIT, perf_gtk__signal);\n-\tsignal(SIGTERM, perf_gtk__signal);\n+\tsignal(SIGSEGV, perf_gtk__fatal_signal);\n+\tsignal(SIGFPE,  perf_gtk__fatal_signal);\n+\tperf_gtk__install_quit_signals();\n \n-\twindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);\n+\twindow = gtk_window_new();\n \n \tgtk_window_set_title(GTK_WINDOW(window), \"perf report\");\n \n-\tg_signal_connect(window, \"delete_event\", gtk_main_quit, NULL);\n-\n \tpgctx = perf_gtk__activate_context(window);\n \tif (!pgctx)\n \t\treturn -1;\n \n-\tvbox = gtk_vbox_new(FALSE, 0);\n+\tvbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);\n \n \tnotebook = gtk_notebook_new();\n \n-\tgtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);\n+\tgtk_widget_set_vexpand(notebook, TRUE);\n+\tgtk_box_append(GTK_BOX(vbox), notebook);\n \n \tinfo_bar = perf_gtk__setup_info_bar();\n-\tif (info_bar)\n-\t\tgtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0);\n+\tgtk_box_append(GTK_BOX(vbox), info_bar);\n \n \tstatbar = perf_gtk__setup_statusbar();\n-\tgtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);\n+\tgtk_box_append(GTK_BOX(vbox), statbar);\n \n-\tgtk_container_add(GTK_CONTAINER(window), vbox);\n+\tgtk_window_set_child(GTK_WINDOW(window), vbox);\n \n \tevlist__for_each_entry(evlist, pos) {\n \t\tstruct hists *hists = evsel__hists(pos);\n@@ -649,7 +656,7 @@ int evlist__gtk_browse_hists(struct evlist *evlist, const char *help,\n \t\t\t}\n \t\t}\n \n-\t\tscrolled_window = gtk_scrolled_window_new(NULL, NULL);\n+\t\tscrolled_window = gtk_scrolled_window_new();\n \n \t\tgtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),\n \t\t\t\t\t\t\tGTK_POLICY_AUTOMATIC,\n@@ -665,15 +672,12 @@ int evlist__gtk_browse_hists(struct evlist *evlist, const char *help,\n \t\tgtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);\n \t}\n \n-\tgtk_widget_show_all(window);\n-\n \tperf_gtk__resize_window(window);\n-\n-\tgtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);\n+\tgtk_widget_set_visible(window, TRUE);\n \n \tui_helpline__push(help);\n \n-\tgtk_main();\n+\tperf_gtk__run_main_loop(window);\n \n \tperf_gtk__deactivate_context(\u0026pgctx);\n \ndiff --git a/tools/perf/ui/gtk/progress.c b/tools/perf/ui/gtk/progress.c\nindex eea6fcde518a8..770f9251b54b7 100644\n--- a/tools/perf/ui/gtk/progress.c\n+++ b/tools/perf/ui/gtk/progress.c\n@@ -1,49 +1,65 @@\n // SPDX-License-Identifier: GPL-2.0\n #include \u003cinttypes.h\u003e\n+#include \u003cstdio.h\u003e\n \n #include \"gtk.h\"\n #include \"../progress.h\"\n+#include \u003clinux/compiler.h\u003e\n \n static GtkWidget *dialog;\n static GtkWidget *progress;\n \n+static void gtk_ui_progress__destroyed(GtkWidget *widget __maybe_unused,\n+\t\t\t\t\tgpointer data __maybe_unused)\n+{\n+\tdialog = NULL;\n+\tprogress = NULL;\n+}\n+\n static void gtk_ui_progress__update(struct ui_progress *p)\n {\n \tdouble fraction = p-\u003etotal ? 1.0 * p-\u003ecurr / p-\u003etotal : 0.0;\n \tchar buf[1024];\n \n \tif (dialog == NULL) {\n-\t\tGtkWidget *vbox = gtk_vbox_new(TRUE, 5);\n+\t\tGtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);\n \t\tGtkWidget *label = gtk_label_new(p-\u003etitle);\n \n-\t\tdialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);\n+\t\tdialog = gtk_window_new();\n \t\tprogress = gtk_progress_bar_new();\n \n-\t\tgtk_box_pack_start(GTK_BOX(vbox), label, TRUE, FALSE, 3);\n-\t\tgtk_box_pack_start(GTK_BOX(vbox), progress, TRUE, TRUE, 3);\n+\t\tgtk_widget_set_vexpand(label, TRUE);\n+\t\tgtk_box_append(GTK_BOX(vbox), label);\n+\t\tgtk_widget_set_vexpand(progress, TRUE);\n+\t\tgtk_box_append(GTK_BOX(vbox), progress);\n+\n+\t\tgtk_window_set_child(GTK_WINDOW(dialog), vbox);\n \n-\t\tgtk_container_add(GTK_CONTAINER(dialog), vbox);\n+\t\tg_signal_connect(dialog, \"destroy\",\n+\t\t\t\t G_CALLBACK(gtk_ui_progress__destroyed), NULL);\n \n \t\tgtk_window_set_title(GTK_WINDOW(dialog), \"perf\");\n-\t\tgtk_window_resize(GTK_WINDOW(dialog), 300, 80);\n-\t\tgtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);\n+\t\tgtk_window_set_default_size(GTK_WINDOW(dialog), 300, 80);\n \n-\t\tgtk_widget_show_all(dialog);\n+\t\tgtk_widget_set_visible(dialog, TRUE);\n \t}\n \n \tgtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), fraction);\n \tsnprintf(buf, sizeof(buf), \"%\"PRIu64\" / %\"PRIu64, p-\u003ecurr, p-\u003etotal);\n \tgtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), buf);\n \n-\t/* we didn't call gtk_main yet, so do it manually */\n-\twhile (gtk_events_pending())\n-\t\tgtk_main_iteration();\n+\t/* we didn't start a main loop yet, so pump events manually */\n+\twhile (g_main_context_pending(NULL))\n+\t\tg_main_context_iteration(NULL, FALSE);\n }\n \n static void gtk_ui_progress__finish(void)\n {\n+\tif (dialog == NULL)\n+\t\treturn;\n+\n \t/* this will also destroy all of its children */\n-\tgtk_widget_destroy(dialog);\n+\tgtk_window_destroy(GTK_WINDOW(dialog));\n \n \tdialog = NULL;\n }\ndiff --git a/tools/perf/ui/gtk/setup.c b/tools/perf/ui/gtk/setup.c\nindex f5eee4d668738..9b44f37197477 100644\n--- a/tools/perf/ui/gtk/setup.c\n+++ b/tools/perf/ui/gtk/setup.c\n@@ -12,7 +12,7 @@ int perf_gtk__init(void)\n \tgtk_ui_progress__init();\n \tperf_gtk__init_hpp();\n \n-\treturn gtk_init_check(NULL, NULL) ? 0 : -1;\n+\treturn gtk_init_check() ? 0 : -1;\n }\n \n void perf_gtk__exit(bool wait_for_ok __maybe_unused)\n@@ -20,5 +20,6 @@ void perf_gtk__exit(bool wait_for_ok __maybe_unused)\n \tif (!perf_gtk__is_active_context(pgctx))\n \t\treturn;\n \tperf_error__unregister(\u0026perf_gtk_eops);\n-\tgtk_main_quit();\n+\tperf_gtk__quit_error_dialog();\n+\tperf_gtk__quit_main_loop();\n }\ndiff --git a/tools/perf/ui/gtk/util.c b/tools/perf/ui/gtk/util.c\nindex c47f5c387838c..5823ff0d7c2a7 100644\n--- a/tools/perf/ui/gtk/util.c\n+++ b/tools/perf/ui/gtk/util.c\n@@ -2,8 +2,10 @@\n #include \"../util.h\"\n #include \"gtk.h\"\n \n+#include \u003cstdarg.h\u003e\n+#include \u003cstdio.h\u003e\n #include \u003cstdlib.h\u003e\n-#include \u003cstring.h\u003e\n+#include \u003clinux/compiler.h\u003e\n #include \u003clinux/zalloc.h\u003e\n \n struct perf_gtk_context *pgctx;\n@@ -28,86 +30,103 @@ int perf_gtk__deactivate_context(struct perf_gtk_context **ctx)\n \treturn 0;\n }\n \n+/*\n+ * perf_gtk__error() can be called re-entrantly, since the dialog isn't\n+ * modal and its nested loop still pumps events for the main window.\n+ * Track every currently running loop instead of a single pointer, so a\n+ * nested call can't clobber an outer call's loop and leak it.\n+ */\n+static GSList *perf_gtk__error_loops;\n+\n+static void perf_gtk__quit_loop(gpointer data, gpointer user_data __maybe_unused)\n+{\n+\tg_main_loop_quit(data);\n+}\n+\n+void perf_gtk__quit_error_dialog(void)\n+{\n+\tg_slist_foreach(perf_gtk__error_loops, perf_gtk__quit_loop, NULL);\n+}\n+\n+static void perf_gtk__dialog_response(GtkDialog *dialog,\n+\t\t\t\t      gint response_id __maybe_unused,\n+\t\t\t\t      gpointer data __maybe_unused)\n+{\n+\tgtk_window_destroy(GTK_WINDOW(dialog));\n+}\n+\n static int perf_gtk__error(const char *format, va_list args)\n {\n \tchar *msg;\n \tGtkWidget *dialog;\n+\tGMainLoop *loop;\n+\tva_list args_copy;\n \n+\tva_copy(args_copy, args);\n \tif (!perf_gtk__is_active_context(pgctx) ||\n-\t    vasprintf(\u0026msg, format, args) \u003c 0) {\n+\t    vasprintf(\u0026msg, format, args_copy) \u003c 0) {\n+\t\tva_end(args_copy);\n \t\tfprintf(stderr, \"Error:\\n\");\n \t\tvfprintf(stderr, format, args);\n \t\tfprintf(stderr, \"\\n\");\n \t\treturn -1;\n \t}\n+\tva_end(args_copy);\n \n \tdialog = gtk_message_dialog_new_with_markup(GTK_WINDOW(pgctx-\u003emain_window),\n \t\t\t\t\tGTK_DIALOG_DESTROY_WITH_PARENT,\n \t\t\t\t\tGTK_MESSAGE_ERROR,\n \t\t\t\t\tGTK_BUTTONS_CLOSE,\n \t\t\t\t\t\"\u003cb\u003eError\u003c/b\u003e\\n\\n%s\", msg);\n-\tgtk_dialog_run(GTK_DIALOG(dialog));\n \n-\tgtk_widget_destroy(dialog);\n+\t/*\n+\t * \"response\" only fires when a button is clicked; DESTROY_WITH_PARENT\n+\t * destroys the dialog directly without it. Quit from \"destroy\"\n+\t * instead, which fires either way, so the nested loop below can't\n+\t * outlive the dialog and hang.\n+\t */\n+\tloop = g_main_loop_new(NULL, FALSE);\n+\tperf_gtk__error_loops = g_slist_prepend(perf_gtk__error_loops, loop);\n+\tg_signal_connect(dialog, \"response\",\n+\t\t\t G_CALLBACK(perf_gtk__dialog_response), NULL);\n+\tg_signal_connect_swapped(dialog, \"destroy\",\n+\t\t\t\t G_CALLBACK(g_main_loop_quit), loop);\n+\n+\tgtk_widget_set_visible(dialog, TRUE);\n+\tg_main_loop_run(loop);\n+\tperf_gtk__error_loops = g_slist_remove(perf_gtk__error_loops, loop);\n+\tg_main_loop_unref(loop);\n+\n \tfree(msg);\n \treturn 0;\n }\n \n-#ifdef HAVE_GTK_INFO_BAR_SUPPORT\n static int perf_gtk__warning_info_bar(const char *format, va_list args)\n {\n \tchar *msg;\n+\tva_list args_copy;\n \n+\tva_copy(args_copy, args);\n \tif (!perf_gtk__is_active_context(pgctx) ||\n-\t    vasprintf(\u0026msg, format, args) \u003c 0) {\n+\t    vasprintf(\u0026msg, format, args_copy) \u003c 0) {\n+\t\tva_end(args_copy);\n \t\tfprintf(stderr, \"Warning:\\n\");\n \t\tvfprintf(stderr, format, args);\n \t\tfprintf(stderr, \"\\n\");\n \t\treturn -1;\n \t}\n+\tva_end(args_copy);\n \n \tgtk_label_set_text(GTK_LABEL(pgctx-\u003emessage_label), msg);\n \tgtk_info_bar_set_message_type(GTK_INFO_BAR(pgctx-\u003einfo_bar),\n \t\t\t\t      GTK_MESSAGE_WARNING);\n-\tgtk_widget_show(pgctx-\u003einfo_bar);\n-\n-\tfree(msg);\n-\treturn 0;\n-}\n-#else\n-static int perf_gtk__warning_statusbar(const char *format, va_list args)\n-{\n-\tchar *msg, *p;\n-\n-\tif (!perf_gtk__is_active_context(pgctx) ||\n-\t    vasprintf(\u0026msg, format, args) \u003c 0) {\n-\t\tfprintf(stderr, \"Warning:\\n\");\n-\t\tvfprintf(stderr, format, args);\n-\t\tfprintf(stderr, \"\\n\");\n-\t\treturn -1;\n-\t}\n-\n-\tgtk_statusbar_pop(GTK_STATUSBAR(pgctx-\u003estatbar),\n-\t\t\t  pgctx-\u003estatbar_ctx_id);\n-\n-\t/* Only first line can be displayed */\n-\tp = strchr(msg, '\\n');\n-\tif (p)\n-\t\t*p = '\\0';\n-\n-\tgtk_statusbar_push(GTK_STATUSBAR(pgctx-\u003estatbar),\n-\t\t\t   pgctx-\u003estatbar_ctx_id, msg);\n+\tgtk_widget_set_visible(pgctx-\u003einfo_bar, TRUE);\n \n \tfree(msg);\n \treturn 0;\n }\n-#endif\n \n struct perf_error_ops perf_gtk_eops = {\n \t.error\t\t= perf_gtk__error,\n-#ifdef HAVE_GTK_INFO_BAR_SUPPORT\n \t.warning\t= perf_gtk__warning_info_bar,\n-#else\n-\t.warning\t= perf_gtk__warning_statusbar,\n-#endif\n };\ndiff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c\nindex ff800047e697f..d887346c7a632 100644\n--- a/tools/perf/ui/setup.c\n+++ b/tools/perf/ui/setup.c\n@@ -14,7 +14,7 @@ int use_browser = -1;\n \n #define PERF_GTK_DSO \"libperf-gtk.so\"\n \n-#ifdef HAVE_GTK2_SUPPORT\n+#ifdef HAVE_GTK4_SUPPORT\n \n static int setup_gtk_browser(void)\n {\ndiff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c\nindex df70e95a84704..123b6d5fcea78 100644\n--- a/tools/perf/util/annotate.c\n+++ b/tools/perf/util/annotate.c\n@@ -145,6 +145,17 @@ static int annotated_source__alloc_histograms(struct annotated_source *src,\n \treturn src-\u003ehistograms ? 0 : -1;\n }\n \n+struct sym_hist_entry *\n+annotated_source__hist_entry(struct annotated_source *src, const struct evsel *evsel, u64 offset)\n+{\n+\tstruct sym_hist_entry *entry;\n+\tlong key = offset \u003c\u003c 16 | evsel-\u003ecore.idx;\n+\n+\tif (!hashmap__find(src-\u003esamples, key, \u0026entry))\n+\t\treturn NULL;\n+\treturn entry;\n+}\n+\n void symbol__annotate_zero_histograms(struct symbol *sym)\n {\n \tstruct annotation *notes = symbol__annotation(sym);\ndiff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h\nindex fa08d09b80f76..40038a3779d46 100644\n--- a/tools/perf/util/annotate.h\n+++ b/tools/perf/util/annotate.h\n@@ -406,16 +406,8 @@ static inline struct sym_hist *annotation__histogram(struct annotation *notes,\n \treturn annotated_source__histogram(notes-\u003esrc, evsel);\n }\n \n-static inline struct sym_hist_entry *\n-annotated_source__hist_entry(struct annotated_source *src, const struct evsel *evsel, u64 offset)\n-{\n-\tstruct sym_hist_entry *entry;\n-\tlong key = offset \u003c\u003c 16 | evsel-\u003ecore.idx;\n-\n-\tif (!hashmap__find(src-\u003esamples, key, \u0026entry))\n-\t\treturn NULL;\n-\treturn entry;\n-}\n+struct sym_hist_entry *\n+annotated_source__hist_entry(struct annotated_source *src, const struct evsel *evsel, u64 offset);\n \n static inline struct annotation *symbol__annotation(struct symbol *sym)\n {\n"
}

2/1 2026/09/07 02:26 agent patch-evaluator
1m Model:
core

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch series modifies only user-space tooling and build scripts under tools/ (specifically porting the perf GTK UI from GTK2 to GTK4 and minor helper relocation in tools/perf/util/annotate.c). It does not alter any kernel-space code, the compiled kernel image (vmlinux), or kernel modules.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 5faf41536400fc72093df78d6e797a37cbab06f7
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Sep 7 02:26:22 2026 +0000

    syz-cluster: applied patch under review

diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index 99eb0ea095375..4ec95c35a5c19 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -113,8 +113,7 @@ FEATURE_TESTS_EXTRA :=                  \
          compile-x32                    \
          cplus-demangle                 \
          cxa-demangle                   \
-         gtk2                           \
-         gtk2-infobar                   \
+         gtk4                           \
          hello                          \
          babeltrace2-ctf-writer         \
          libcapstone                    \
@@ -143,6 +142,7 @@ endif
 FEATURE_DISPLAY ?=              \
          libdw                  \
          glibc                  \
+         gtk4                   \
          libelf                 \
          libnuma                \
          numa_num_possible_cpus \
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index 7d165018116a5..01c48e6ef021d 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -11,8 +11,7 @@ FILES=                                          \
          test-eventfd.bin                       \
          test-fortify-source.bin                \
          test-glibc.bin                         \
-         test-gtk2.bin                          \
-         test-gtk2-infobar.bin                  \
+         test-gtk4.bin                          \
          test-hello.bin                         \
          test-libbfd.bin                        \
 	 test-libbfd-threadsafe.bin      	\
@@ -240,11 +239,8 @@ $(OUTPUT)test-libcpupower.bin:
 $(OUTPUT)test-libtracefs.bin:
 	 $(BUILD) $(shell $(PKG_CONFIG) --cflags libtracefs 2>/dev/null) -ltracefs
 
-$(OUTPUT)test-gtk2.bin:
-	$(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) -Wno-deprecated-declarations
-
-$(OUTPUT)test-gtk2-infobar.bin:
-	$(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null)
+$(OUTPUT)test-gtk4.bin:
+	$(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk4 2>/dev/null)
 
 grep-libs  = $(filter -l%,$(1))
 strip-libs = $(filter-out -l%,$(1))
diff --git a/tools/build/feature/test-gtk2-infobar.c b/tools/build/feature/test-gtk2-infobar.c
deleted file mode 100644
index b1b716dd57330..0000000000000
--- a/tools/build/feature/test-gtk2-infobar.c
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#pragma GCC diagnostic ignored "-Wstrict-prototypes"
-#include <gtk/gtk.h>
-#pragma GCC diagnostic error "-Wstrict-prototypes"
-
-int main(int argc, char *argv[])
-{
-	gtk_init(&argc, &argv);
-	gtk_info_bar_new();
-
-	return 0;
-}
diff --git a/tools/build/feature/test-gtk2.c b/tools/build/feature/test-gtk4.c
similarity index 76%
rename from tools/build/feature/test-gtk2.c
rename to tools/build/feature/test-gtk4.c
index 2aaf4bfc20556..b9520e7408b99 100644
--- a/tools/build/feature/test-gtk2.c
+++ b/tools/build/feature/test-gtk4.c
@@ -3,9 +3,9 @@
 #include <gtk/gtk.h>
 #pragma GCC diagnostic error "-Wstrict-prototypes"
 
-int main(int argc, char *argv[])
+int main(void)
 {
-	gtk_init(&argc, &argv);
+	gtk_init();
 
         return 0;
 }
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 22f87eaa32796..7af9b3f81c065 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -351,7 +351,7 @@ OPTIONS
 	requires a tty, if one is not present, as when piping to other
 	commands, the stdio interface is used.
 
---gtk:: Use the GTK2 interface.
+--gtk:: Use the GTK4 interface.
 
 -k::
 --vmlinux=<file>::
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 5b713837eede7..56014106479a3 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -106,7 +106,7 @@ clean:
 # make -C tools/perf -f tests/make
 #
 build-test:
-	@$(MAKE) SHUF=1 -f tests/make REUSE_FEATURES_DUMP=1 MK=Makefile SET_PARALLEL=1 --no-print-directory tarpkg make_static make_with_gtk2 out
+	@$(MAKE) SHUF=1 -f tests/make REUSE_FEATURES_DUMP=1 MK=Makefile SET_PARALLEL=1 --no-print-directory tarpkg make_static make_with_gtk4 out
 
 build-test-tarball:
 	@$(MAKE) -f tests/make REUSE_FEATURES_DUMP=1 MK=Makefile SET_PARALLEL=1 --no-print-directory out
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index 4d5993da9f945..4ee7393a39f91 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -774,21 +774,22 @@ ifndef NO_SLANG
   endif
 endif
 
-ifdef GTK2
-  FLAGS_GTK2=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null)
-  $(call feature_check,gtk2)
-  ifneq ($(feature-gtk2), 1)
-    $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev)
-    NO_GTK2 := 1
+ifdef GTK4
+  FLAGS_GTK4=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) $(shell $(PKG_CONFIG) --libs --cflags gtk4 2>/dev/null)
+  $(call feature_check,gtk4)
+  ifneq ($(feature-gtk4), 1)
+    $(warning GTK4 not found, disables GTK4 support. Please install gtk4-devel or libgtk-4-dev)
+    NO_GTK4 := 1
   else
-    $(call feature_check,gtk2-infobar)
-    ifeq ($(feature-gtk2-infobar), 1)
-      GTK_CFLAGS := -DHAVE_GTK_INFO_BAR_SUPPORT
-    endif
-    CFLAGS += -DHAVE_GTK2_SUPPORT
-    GTK_CFLAGS += $(shell $(PKG_CONFIG) --cflags gtk+-2.0 2>/dev/null)
-    GTK_LIBS := $(shell $(PKG_CONFIG) --libs gtk+-2.0 2>/dev/null)
+    CFLAGS += -DHAVE_GTK4_SUPPORT
+    GTK_CFLAGS += $(shell $(PKG_CONFIG) --cflags gtk4 2>/dev/null)
+    GTK_LIBS := $(shell $(PKG_CONFIG) --libs gtk4 2>/dev/null)
     EXTLIBS += -ldl
+    # libperf-gtk.so is dlopen()ed at runtime and calls back into
+    # symbols defined in the perf binary itself (callchain_param,
+    # symbol_conf, evsel__name, ...): perf needs to export those
+    # dynamically for the plugin to resolve them.
+    LDFLAGS += -rdynamic
   endif
 endif
 
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 29cfd44c427f3..2438b40eaaec1 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -49,7 +49,7 @@ include ../scripts/utilities.mak
 #
 # Define NO_SLANG if you do not want TUI support.
 #
-# Define GTK2 if you want GTK+ GUI support.
+# Define GTK4 if you want GTK+ GUI support.
 #
 # Define NO_DEMANGLE if you do not want C++ symbol demangling.
 #
@@ -473,7 +473,7 @@ ifneq ($(OUTPUT),)
   CFLAGS += -I$(OUTPUT)
 endif
 
-ifdef GTK2
+ifdef GTK4
   ALL_PROGRAMS += $(OUTPUT)libperf-gtk.so
   GTK_IN := $(OUTPUT)gtk-in.o
 endif
@@ -811,7 +811,7 @@ check: prepare
 
 ### Installation rules
 
-ifdef GTK2
+ifdef GTK4
 install-gtk: $(OUTPUT)libperf-gtk.so
 	$(call QUIET_INSTALL, 'GTK UI') \
 		$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(libdir_SQ)'; \
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 69cb72b2082a6..15163e081a8ce 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -52,7 +52,7 @@ struct perf_annotate {
 	bool	   use_tui;
 #endif
 	bool	   use_stdio, use_stdio2;
-#ifdef HAVE_GTK2_SUPPORT
+#ifdef HAVE_GTK4_SUPPORT
 	bool	   use_gtk;
 #endif
 	bool	   skip_missing;
@@ -712,7 +712,7 @@ int cmd_annotate(int argc, const char **argv)
 	OPT_BOOLEAN('q', "quiet", &quiet, "do now show any warnings or messages"),
 	OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
 		    "dump raw trace in ASCII"),
-#ifdef HAVE_GTK2_SUPPORT
+#ifdef HAVE_GTK4_SUPPORT
 	OPT_BOOLEAN(0, "gtk", &annotate.use_gtk, "Use the GTK interface"),
 #endif
 #ifdef HAVE_SLANG_SUPPORT
@@ -828,7 +828,7 @@ int cmd_annotate(int argc, const char **argv)
 	if (annotate_check_args() < 0)
 		return -EINVAL;
 
-#ifdef HAVE_GTK2_SUPPORT
+#ifdef HAVE_GTK4_SUPPORT
 	if (symbol_conf.show_nr_samples && annotate.use_gtk) {
 		pr_err("--show-nr-samples is not available in --gtk mode at this time\n");
 		return ret;
@@ -898,7 +898,7 @@ int cmd_annotate(int argc, const char **argv)
 	else if (annotate.use_tui)
 		use_browser = 1;
 #endif
-#ifdef HAVE_GTK2_SUPPORT
+#ifdef HAVE_GTK4_SUPPORT
 	else if (annotate.use_gtk)
 		use_browser = 2;
 #endif
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 60d1f166629e0..d14384c584661 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -82,7 +82,7 @@ struct report {
 #ifdef HAVE_SLANG_SUPPORT
 	bool			use_tui;
 #endif
-#ifdef HAVE_GTK2_SUPPORT
+#ifdef HAVE_GTK4_SUPPORT
 	bool			use_gtk;
 #endif
 	bool			use_stdio;
@@ -1359,8 +1359,8 @@ int cmd_report(int argc, const char **argv)
 #ifdef HAVE_SLANG_SUPPORT
 	OPT_BOOLEAN(0, "tui", &report.use_tui, "Use the TUI interface"),
 #endif
-#ifdef HAVE_GTK2_SUPPORT
-	OPT_BOOLEAN(0, "gtk", &report.use_gtk, "Use the GTK2 interface"),
+#ifdef HAVE_GTK4_SUPPORT
+	OPT_BOOLEAN(0, "gtk", &report.use_gtk, "Use the GTK4 interface"),
 #endif
 	OPT_BOOLEAN(0, "stdio", &report.use_stdio,
 		    "Use the stdio interface"),
@@ -1710,7 +1710,7 @@ int cmd_report(int argc, const char **argv)
 	else if (report.use_tui)
 		use_browser = 1;
 #endif
-#ifdef HAVE_GTK2_SUPPORT
+#ifdef HAVE_GTK4_SUPPORT
 	else if (report.use_gtk)
 		use_browser = 2;
 #endif
diff --git a/tools/perf/scripts/install-build-deps.sh b/tools/perf/scripts/install-build-deps.sh
index d003e7fab2be8..a601a5260c17e 100755
--- a/tools/perf/scripts/install-build-deps.sh
+++ b/tools/perf/scripts/install-build-deps.sh
@@ -199,8 +199,8 @@ fedora_pkg_for() {
 	# opt-in features, which a default build does not enable: the libbfd
 	# disassembler family (libbfd, libbfd-threadsafe, libbfd-liberty,
 	# disassembler-*, cplus-demangle), only linked on BUILD_NONDISTRO
-	# builds and deprecated in favor of capstone, GTK2, LIBPERL and
-	# LIBUNWIND support (ifdef GTK2 / ifdef LIBPERL / LIBUNWIND=1),
+	# builds and deprecated in favor of capstone, GTK4, LIBPERL and
+	# LIBUNWIND support (ifdef GTK4 / ifdef LIBPERL / LIBUNWIND=1),
 	# and CoreSight (ifdef CORESIGHT), are deliberately not mapped.
 	# libaio is not mapped either: its
 	# test uses the POSIX AIO API (aio.h, aio_*, -lrt), provided by
diff --git a/tools/perf/tests/make b/tools/perf/tests/make
index d2c2f526e1db0..202ab55019161 100644
--- a/tools/perf/tests/make
+++ b/tools/perf/tests/make
@@ -95,7 +95,7 @@ make_no_babeltrace2 := NO_BABELTRACE2=1
 make_with_coresight := CORESIGHT=1
 make_no_sdt	    := NO_SDT=1
 make_no_libpfm4     := NO_LIBPFM4=1
-make_with_gtk2      := GTK2=1
+make_with_gtk4      := GTK4=1
 make_refcnt_check   := EXTRA_CFLAGS="-DREFCNT_CHECKING=1"
 make_tags           := tags
 make_cscope         := cscope
@@ -318,7 +318,7 @@ $(run):
 	$(call test,$@) && \
 	rm -rf $@ $$TMP_DEST || (cat $@ ; false)
 
-make_with_gtk2:
+make_with_gtk4:
 	$(call clean)
 	@TMP_DEST=$$(mktemp -d); \
 	cmd="cd $(PERF) && $(MAKE_F) $($@) $(PARALLEL_OPT) $(O_OPT) DESTDIR=$$TMP_DEST"; \
diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c
index 8920e298420a8..094dfa045b774 100644
--- a/tools/perf/ui/gtk/annotate.c
+++ b/tools/perf/ui/gtk/annotate.c
@@ -11,6 +11,7 @@
 #include "ui/helpline.h"
 #include <inttypes.h>
 #include <signal.h>
+#include <string.h>
 
 enum {
 	ANN_COL__PERCENT,
@@ -161,7 +162,7 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct map_symbol *ms,
 			gtk_list_store_set(store, &iter, ANN_COL__LINE, s, -1);
 	}
 
-	gtk_container_add(GTK_CONTAINER(window), view);
+	gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(window), view);
 
 	list_for_each_entry_safe(pos, n, &notes->src->source, al.node) {
 		list_del_init(&pos->al.node);
@@ -205,40 +206,34 @@ static int symbol__gtk_annotate(struct map_symbol *ms, struct evsel *evsel,
 		GtkWidget *infobar;
 		GtkWidget *statbar;
 
-		signal(SIGSEGV, perf_gtk__signal);
-		signal(SIGFPE,  perf_gtk__signal);
-		signal(SIGINT,  perf_gtk__signal);
-		signal(SIGQUIT, perf_gtk__signal);
-		signal(SIGTERM, perf_gtk__signal);
+		signal(SIGSEGV, perf_gtk__fatal_signal);
+		signal(SIGFPE,  perf_gtk__fatal_signal);
+		perf_gtk__install_quit_signals();
 
-		window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+		window = gtk_window_new();
 		gtk_window_set_title(GTK_WINDOW(window), "perf annotate");
 
-		g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
-
 		pgctx = perf_gtk__activate_context(window);
 		if (!pgctx)
 			return -1;
 
-		vbox = gtk_vbox_new(FALSE, 0);
+		vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
 		notebook = gtk_notebook_new();
 		pgctx->notebook = notebook;
 
-		gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
+		gtk_widget_set_vexpand(notebook, TRUE);
+		gtk_box_append(GTK_BOX(vbox), notebook);
 
 		infobar = perf_gtk__setup_info_bar();
-		if (infobar) {
-			gtk_box_pack_start(GTK_BOX(vbox), infobar,
-					   FALSE, FALSE, 0);
-		}
+		gtk_box_append(GTK_BOX(vbox), infobar);
 
 		statbar = perf_gtk__setup_statusbar();
-		gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);
+		gtk_box_append(GTK_BOX(vbox), statbar);
 
-		gtk_container_add(GTK_CONTAINER(window), vbox);
+		gtk_window_set_child(GTK_WINDOW(window), vbox);
 	}
 
-	scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+	scrolled_window = gtk_scrolled_window_new();
 	tab_label = gtk_label_new(sym->name);
 
 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
@@ -267,12 +262,11 @@ void perf_gtk__show_annotations(void)
 		return;
 
 	window = pgctx->main_window;
-	gtk_widget_show_all(window);
 
 	perf_gtk__resize_window(window);
-	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+	gtk_widget_set_visible(window, TRUE);
 
-	gtk_main();
+	perf_gtk__run_main_loop(window);
 
 	perf_gtk__deactivate_context(&pgctx);
 }
diff --git a/tools/perf/ui/gtk/browser.c b/tools/perf/ui/gtk/browser.c
index d2dadf3873fbd..98f1d6bfcb246 100644
--- a/tools/perf/ui/gtk/browser.c
+++ b/tools/perf/ui/gtk/browser.c
@@ -5,32 +5,92 @@
 #include "../hist.h"
 #include "../helpline.h"
 
+#include <glib-unix.h>
 #include <signal.h>
 
-void perf_gtk__signal(int sig)
+/*
+ * SIGINT/SIGQUIT/SIGTERM are asynchronous: deferring the actual exit to a
+ * GLib source dispatched from the main loop means it always runs on the
+ * main thread, serialized with everything else the main loop does
+ * (including perf_gtk__error()'s updates to perf_gtk__error_loops), instead
+ * of racing them from arbitrary signal-handler context.
+ */
+static gboolean perf_gtk__quit_signal(gpointer data __maybe_unused)
 {
 	perf_gtk__exit(false);
+	return G_SOURCE_REMOVE;
+}
+
+void perf_gtk__install_quit_signals(void)
+{
+	g_unix_signal_add(SIGINT,  perf_gtk__quit_signal, NULL);
+	g_unix_signal_add(SIGQUIT, perf_gtk__quit_signal, NULL);
+	g_unix_signal_add(SIGTERM, perf_gtk__quit_signal, NULL);
+}
+
+/*
+ * SIGSEGV/SIGFPE are synchronous faults: there's no "later" to defer to,
+ * and no safe way to run GTK/GLib code (or anything else non-async-signal-
+ * safe) from the faulting context. Report and let the default disposition
+ * (core dump) happen instead of trying to tear down GTK state here.
+ */
+void perf_gtk__fatal_signal(int sig)
+{
 	psignal(sig, "perf");
+	signal(sig, SIG_DFL);
+	raise(sig);
 }
 
 void perf_gtk__resize_window(GtkWidget *window)
 {
 	GdkRectangle rect;
-	GdkScreen *screen;
-	int monitor;
+	GdkMonitor *monitor;
+	GdkDisplay *display;
+	GListModel *monitors;
 	int height;
 	int width;
 
-	screen = gtk_widget_get_screen(window);
+	display = gtk_widget_get_display(window);
+	monitors = gdk_display_get_monitors(display);
+	monitor = g_list_model_get_item(monitors, 0);
+	if (!monitor) {
+		gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
+		return;
+	}
 
-	monitor = gdk_screen_get_monitor_at_window(screen, window->window);
-
-	gdk_screen_get_monitor_geometry(screen, monitor, &rect);
+	gdk_monitor_get_geometry(monitor, &rect);
+	g_object_unref(monitor);
 
 	width	= rect.width * 3 / 4;
 	height	= rect.height * 3 / 4;
 
-	gtk_window_resize(GTK_WINDOW(window), width, height);
+	gtk_window_set_default_size(GTK_WINDOW(window), width, height);
+}
+
+static GMainLoop *perf_gtk__main_loop;
+
+void perf_gtk__quit_main_loop(void)
+{
+	if (perf_gtk__main_loop)
+		g_main_loop_quit(perf_gtk__main_loop);
+}
+
+static gboolean perf_gtk__close_request(GtkWidget *widget __maybe_unused,
+					gpointer data __maybe_unused)
+{
+	perf_gtk__quit_main_loop();
+
+	return FALSE;
+}
+
+void perf_gtk__run_main_loop(GtkWidget *window)
+{
+	g_signal_connect(window, "close-request",
+			 G_CALLBACK(perf_gtk__close_request), NULL);
+
+	perf_gtk__main_loop = g_main_loop_new(NULL, FALSE);
+	g_main_loop_run(perf_gtk__main_loop);
+	g_clear_pointer(&perf_gtk__main_loop, g_main_loop_unref);
 }
 
 const char *perf_gtk__get_percent_color(double percent)
@@ -42,33 +102,35 @@ const char *perf_gtk__get_percent_color(double percent)
 	return NULL;
 }
 
-#ifdef HAVE_GTK_INFO_BAR_SUPPORT
+static void perf_gtk__hide_widget(GtkWidget *widget, gint response_id __maybe_unused,
+				   gpointer data __maybe_unused)
+{
+	gtk_widget_set_visible(widget, FALSE);
+}
+
 GtkWidget *perf_gtk__setup_info_bar(void)
 {
 	GtkWidget *info_bar;
 	GtkWidget *label;
-	GtkWidget *content_area;
 
 	info_bar = gtk_info_bar_new();
-	gtk_widget_set_no_show_all(info_bar, TRUE);
+	gtk_widget_set_visible(info_bar, FALSE);
 
 	label = gtk_label_new("");
-	gtk_widget_show(label);
+	gtk_widget_set_visible(label, TRUE);
 
-	content_area = gtk_info_bar_get_content_area(GTK_INFO_BAR(info_bar));
-	gtk_container_add(GTK_CONTAINER(content_area), label);
+	gtk_info_bar_add_child(GTK_INFO_BAR(info_bar), label);
 
-	gtk_info_bar_add_button(GTK_INFO_BAR(info_bar), GTK_STOCK_OK,
+	gtk_info_bar_add_button(GTK_INFO_BAR(info_bar), "_OK",
 				GTK_RESPONSE_OK);
 	g_signal_connect(info_bar, "response",
-			 G_CALLBACK(gtk_widget_hide), NULL);
+			 G_CALLBACK(perf_gtk__hide_widget), NULL);
 
 	pgctx->info_bar = info_bar;
 	pgctx->message_label = label;
 
 	return info_bar;
 }
-#endif
 
 GtkWidget *perf_gtk__setup_statusbar(void)
 {
diff --git a/tools/perf/ui/gtk/gtk.h b/tools/perf/ui/gtk/gtk.h
index a2b497f03fd6e..6b37fd9943c9d 100644
--- a/tools/perf/ui/gtk/gtk.h
+++ b/tools/perf/ui/gtk/gtk.h
@@ -13,10 +13,8 @@ struct perf_gtk_context {
 	GtkWidget *main_window;
 	GtkWidget *notebook;
 
-#ifdef HAVE_GTK_INFO_BAR_SUPPORT
 	GtkWidget *info_bar;
 	GtkWidget *message_label;
-#endif
 	GtkWidget *statbar;
 	guint statbar_ctx_id;
 };
@@ -38,19 +36,15 @@ void perf_gtk__init_helpline(void);
 void gtk_ui_progress__init(void);
 void perf_gtk__init_hpp(void);
 
-void perf_gtk__signal(int sig);
+void perf_gtk__install_quit_signals(void);
+void perf_gtk__fatal_signal(int sig);
 void perf_gtk__resize_window(GtkWidget *window);
+void perf_gtk__run_main_loop(GtkWidget *window);
+void perf_gtk__quit_main_loop(void);
+void perf_gtk__quit_error_dialog(void);
 const char *perf_gtk__get_percent_color(double percent);
 GtkWidget *perf_gtk__setup_statusbar(void);
-
-#ifdef HAVE_GTK_INFO_BAR_SUPPORT
 GtkWidget *perf_gtk__setup_info_bar(void);
-#else
-static inline GtkWidget *perf_gtk__setup_info_bar(void)
-{
-	return NULL;
-}
-#endif
 
 struct evsel;
 struct evlist;
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index bae21f336ae6d..342d4c3fecb01 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -8,6 +8,8 @@
 #include "../helpline.h"
 #include "../string2.h"
 #include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <linux/string.h>
 
@@ -395,11 +397,9 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
 		}
 	}
 
-	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
-
 	g_signal_connect(view, "row-activated",
 			 G_CALLBACK(on_row_activated), NULL);
-	gtk_container_add(GTK_CONTAINER(window), view);
+	gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(window), view);
 }
 
 static void perf_gtk__add_hierarchy_entries(struct hists *hists,
@@ -449,7 +449,7 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,
 		bf = hpp->buf;
 		size = hpp->size;
 		perf_hpp_list__for_each_format(he->hpp_list, fmt) {
-			int ret;
+			int ret, inc;
 
 			if (fmt->color)
 				ret = fmt->color(fmt, hpp, he);
@@ -457,7 +457,18 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists,
 				ret = fmt->entry(fmt, hpp, he);
 
 			snprintf(hpp->buf + ret, hpp->size - ret, "  ");
-			advance_hpp(hpp, ret + 2);
+			/*
+			 * ret can be as large as hpp->size - 1, so ret + 2
+			 * can exceed hpp->size. advance_hpp() doesn't clamp,
+			 * so passing that through would underflow the
+			 * size_t hpp->size and let a later fmt->entry() in
+			 * this loop write past the end of the caller's
+			 * stack buffer.
+			 */
+			inc = ret + 2;
+			if (inc > (int)hpp->size)
+				inc = hpp->size;
+			advance_hpp(hpp, inc);
 		}
 
 		gtk_tree_store_set(store, &iter, col_idx, strim(bf), -1);
@@ -505,6 +516,7 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,
 	GtkWidget *view;
 	int col_idx;
 	int nr_cols = 0;
+	int ret;
 	char s[512];
 	char buf[512];
 	bool first_node, first_col;
@@ -541,9 +553,10 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,
 	/* construct merged column header since sort keys share single column */
 	buf[0] = '\0';
 	first_node = true;
+	ret = 0;
 	list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
 		if (!first_node)
-			strcat(buf, " / ");
+			ret += scnprintf(buf + ret, sizeof(buf) - ret, " / ");
 		first_node = false;
 
 		first_col = true;
@@ -552,11 +565,11 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,
 				continue;
 
 			if (!first_col)
-				strcat(buf, "+");
+				ret += scnprintf(buf + ret, sizeof(buf) - ret, "+");
 			first_col = false;
 
 			fmt->header(fmt, &hpp, hists, 0, NULL);
-			strcat(buf, strim(hpp.buf));
+			ret += scnprintf(buf + ret, sizeof(buf) - ret, "%s", strim(hpp.buf));
 		}
 	}
 
@@ -583,11 +596,9 @@ static void perf_gtk__show_hierarchy(GtkWidget *window, struct hists *hists,
 	perf_gtk__add_hierarchy_entries(hists, &hists->entries, store,
 					NULL, &hpp, min_pcnt);
 
-	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
-
 	g_signal_connect(view, "row-activated",
 			 G_CALLBACK(on_row_activated), NULL);
-	gtk_container_add(GTK_CONTAINER(window), view);
+	gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(window), view);
 }
 
 int evlist__gtk_browse_hists(struct evlist *evlist, const char *help,
@@ -600,36 +611,32 @@ int evlist__gtk_browse_hists(struct evlist *evlist, const char *help,
 	GtkWidget *statbar;
 	GtkWidget *window;
 
-	signal(SIGSEGV, perf_gtk__signal);
-	signal(SIGFPE,  perf_gtk__signal);
-	signal(SIGINT,  perf_gtk__signal);
-	signal(SIGQUIT, perf_gtk__signal);
-	signal(SIGTERM, perf_gtk__signal);
+	signal(SIGSEGV, perf_gtk__fatal_signal);
+	signal(SIGFPE,  perf_gtk__fatal_signal);
+	perf_gtk__install_quit_signals();
 
-	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	window = gtk_window_new();
 
 	gtk_window_set_title(GTK_WINDOW(window), "perf report");
 
-	g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
-
 	pgctx = perf_gtk__activate_context(window);
 	if (!pgctx)
 		return -1;
 
-	vbox = gtk_vbox_new(FALSE, 0);
+	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
 
 	notebook = gtk_notebook_new();
 
-	gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
+	gtk_widget_set_vexpand(notebook, TRUE);
+	gtk_box_append(GTK_BOX(vbox), notebook);
 
 	info_bar = perf_gtk__setup_info_bar();
-	if (info_bar)
-		gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0);
+	gtk_box_append(GTK_BOX(vbox), info_bar);
 
 	statbar = perf_gtk__setup_statusbar();
-	gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0);
+	gtk_box_append(GTK_BOX(vbox), statbar);
 
-	gtk_container_add(GTK_CONTAINER(window), vbox);
+	gtk_window_set_child(GTK_WINDOW(window), vbox);
 
 	evlist__for_each_entry(evlist, pos) {
 		struct hists *hists = evsel__hists(pos);
@@ -649,7 +656,7 @@ int evlist__gtk_browse_hists(struct evlist *evlist, const char *help,
 			}
 		}
 
-		scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+		scrolled_window = gtk_scrolled_window_new();
 
 		gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
 							GTK_POLICY_AUTOMATIC,
@@ -665,15 +672,12 @@ int evlist__gtk_browse_hists(struct evlist *evlist, const char *help,
 		gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label);
 	}
 
-	gtk_widget_show_all(window);
-
 	perf_gtk__resize_window(window);
-
-	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+	gtk_widget_set_visible(window, TRUE);
 
 	ui_helpline__push(help);
 
-	gtk_main();
+	perf_gtk__run_main_loop(window);
 
 	perf_gtk__deactivate_context(&pgctx);
 
diff --git a/tools/perf/ui/gtk/progress.c b/tools/perf/ui/gtk/progress.c
index eea6fcde518a8..770f9251b54b7 100644
--- a/tools/perf/ui/gtk/progress.c
+++ b/tools/perf/ui/gtk/progress.c
@@ -1,49 +1,65 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <inttypes.h>
+#include <stdio.h>
 
 #include "gtk.h"
 #include "../progress.h"
+#include <linux/compiler.h>
 
 static GtkWidget *dialog;
 static GtkWidget *progress;
 
+static void gtk_ui_progress__destroyed(GtkWidget *widget __maybe_unused,
+					gpointer data __maybe_unused)
+{
+	dialog = NULL;
+	progress = NULL;
+}
+
 static void gtk_ui_progress__update(struct ui_progress *p)
 {
 	double fraction = p->total ? 1.0 * p->curr / p->total : 0.0;
 	char buf[1024];
 
 	if (dialog == NULL) {
-		GtkWidget *vbox = gtk_vbox_new(TRUE, 5);
+		GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
 		GtkWidget *label = gtk_label_new(p->title);
 
-		dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+		dialog = gtk_window_new();
 		progress = gtk_progress_bar_new();
 
-		gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, FALSE, 3);
-		gtk_box_pack_start(GTK_BOX(vbox), progress, TRUE, TRUE, 3);
+		gtk_widget_set_vexpand(label, TRUE);
+		gtk_box_append(GTK_BOX(vbox), label);
+		gtk_widget_set_vexpand(progress, TRUE);
+		gtk_box_append(GTK_BOX(vbox), progress);
+
+		gtk_window_set_child(GTK_WINDOW(dialog), vbox);
 
-		gtk_container_add(GTK_CONTAINER(dialog), vbox);
+		g_signal_connect(dialog, "destroy",
+				 G_CALLBACK(gtk_ui_progress__destroyed), NULL);
 
 		gtk_window_set_title(GTK_WINDOW(dialog), "perf");
-		gtk_window_resize(GTK_WINDOW(dialog), 300, 80);
-		gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
+		gtk_window_set_default_size(GTK_WINDOW(dialog), 300, 80);
 
-		gtk_widget_show_all(dialog);
+		gtk_widget_set_visible(dialog, TRUE);
 	}
 
 	gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), fraction);
 	snprintf(buf, sizeof(buf), "%"PRIu64" / %"PRIu64, p->curr, p->total);
 	gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), buf);
 
-	/* we didn't call gtk_main yet, so do it manually */
-	while (gtk_events_pending())
-		gtk_main_iteration();
+	/* we didn't start a main loop yet, so pump events manually */
+	while (g_main_context_pending(NULL))
+		g_main_context_iteration(NULL, FALSE);
 }
 
 static void gtk_ui_progress__finish(void)
 {
+	if (dialog == NULL)
+		return;
+
 	/* this will also destroy all of its children */
-	gtk_widget_destroy(dialog);
+	gtk_window_destroy(GTK_WINDOW(dialog));
 
 	dialog = NULL;
 }
diff --git a/tools/perf/ui/gtk/setup.c b/tools/perf/ui/gtk/setup.c
index f5eee4d668738..9b44f37197477 100644
--- a/tools/perf/ui/gtk/setup.c
+++ b/tools/perf/ui/gtk/setup.c
@@ -12,7 +12,7 @@ int perf_gtk__init(void)
 	gtk_ui_progress__init();
 	perf_gtk__init_hpp();
 
-	return gtk_init_check(NULL, NULL) ? 0 : -1;
+	return gtk_init_check() ? 0 : -1;
 }
 
 void perf_gtk__exit(bool wait_for_ok __maybe_unused)
@@ -20,5 +20,6 @@ void perf_gtk__exit(bool wait_for_ok __maybe_unused)
 	if (!perf_gtk__is_active_context(pgctx))
 		return;
 	perf_error__unregister(&perf_gtk_eops);
-	gtk_main_quit();
+	perf_gtk__quit_error_dialog();
+	perf_gtk__quit_main_loop();
 }
diff --git a/tools/perf/ui/gtk/util.c b/tools/perf/ui/gtk/util.c
index c47f5c387838c..5823ff0d7c2a7 100644
--- a/tools/perf/ui/gtk/util.c
+++ b/tools/perf/ui/gtk/util.c
@@ -2,8 +2,10 @@
 #include "../util.h"
 #include "gtk.h"
 
+#include <stdarg.h>
+#include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
+#include <linux/compiler.h>
 #include <linux/zalloc.h>
 
 struct perf_gtk_context *pgctx;
@@ -28,86 +30,103 @@ int perf_gtk__deactivate_context(struct perf_gtk_context **ctx)
 	return 0;
 }
 
+/*
+ * perf_gtk__error() can be called re-entrantly, since the dialog isn't
+ * modal and its nested loop still pumps events for the main window.
+ * Track every currently running loop instead of a single pointer, so a
+ * nested call can't clobber an outer call's loop and leak it.
+ */
+static GSList *perf_gtk__error_loops;
+
+static void perf_gtk__quit_loop(gpointer data, gpointer user_data __maybe_unused)
+{
+	g_main_loop_quit(data);
+}
+
+void perf_gtk__quit_error_dialog(void)
+{
+	g_slist_foreach(perf_gtk__error_loops, perf_gtk__quit_loop, NULL);
+}
+
+static void perf_gtk__dialog_response(GtkDialog *dialog,
+				      gint response_id __maybe_unused,
+				      gpointer data __maybe_unused)
+{
+	gtk_window_destroy(GTK_WINDOW(dialog));
+}
+
 static int perf_gtk__error(const char *format, va_list args)
 {
 	char *msg;
 	GtkWidget *dialog;
+	GMainLoop *loop;
+	va_list args_copy;
 
+	va_copy(args_copy, args);
 	if (!perf_gtk__is_active_context(pgctx) ||
-	    vasprintf(&msg, format, args) < 0) {
+	    vasprintf(&msg, format, args_copy) < 0) {
+		va_end(args_copy);
 		fprintf(stderr, "Error:\n");
 		vfprintf(stderr, format, args);
 		fprintf(stderr, "\n");
 		return -1;
 	}
+	va_end(args_copy);
 
 	dialog = gtk_message_dialog_new_with_markup(GTK_WINDOW(pgctx->main_window),
 					GTK_DIALOG_DESTROY_WITH_PARENT,
 					GTK_MESSAGE_ERROR,
 					GTK_BUTTONS_CLOSE,
 					"<b>Error</b>\n\n%s", msg);
-	gtk_dialog_run(GTK_DIALOG(dialog));
 
-	gtk_widget_destroy(dialog);
+	/*
+	 * "response" only fires when a button is clicked; DESTROY_WITH_PARENT
+	 * destroys the dialog directly without it. Quit from "destroy"
+	 * instead, which fires either way, so the nested loop below can't
+	 * outlive the dialog and hang.
+	 */
+	loop = g_main_loop_new(NULL, FALSE);
+	perf_gtk__error_loops = g_slist_prepend(perf_gtk__error_loops, loop);
+	g_signal_connect(dialog, "response",
+			 G_CALLBACK(perf_gtk__dialog_response), NULL);
+	g_signal_connect_swapped(dialog, "destroy",
+				 G_CALLBACK(g_main_loop_quit), loop);
+
+	gtk_widget_set_visible(dialog, TRUE);
+	g_main_loop_run(loop);
+	perf_gtk__error_loops = g_slist_remove(perf_gtk__error_loops, loop);
+	g_main_loop_unref(loop);
+
 	free(msg);
 	return 0;
 }
 
-#ifdef HAVE_GTK_INFO_BAR_SUPPORT
 static int perf_gtk__warning_info_bar(const char *format, va_list args)
 {
 	char *msg;
+	va_list args_copy;
 
+	va_copy(args_copy, args);
 	if (!perf_gtk__is_active_context(pgctx) ||
-	    vasprintf(&msg, format, args) < 0) {
+	    vasprintf(&msg, format, args_copy) < 0) {
+		va_end(args_copy);
 		fprintf(stderr, "Warning:\n");
 		vfprintf(stderr, format, args);
 		fprintf(stderr, "\n");
 		return -1;
 	}
+	va_end(args_copy);
 
 	gtk_label_set_text(GTK_LABEL(pgctx->message_label), msg);
 	gtk_info_bar_set_message_type(GTK_INFO_BAR(pgctx->info_bar),
 				      GTK_MESSAGE_WARNING);
-	gtk_widget_show(pgctx->info_bar);
-
-	free(msg);
-	return 0;
-}
-#else
-static int perf_gtk__warning_statusbar(const char *format, va_list args)
-{
-	char *msg, *p;
-
-	if (!perf_gtk__is_active_context(pgctx) ||
-	    vasprintf(&msg, format, args) < 0) {
-		fprintf(stderr, "Warning:\n");
-		vfprintf(stderr, format, args);
-		fprintf(stderr, "\n");
-		return -1;
-	}
-
-	gtk_statusbar_pop(GTK_STATUSBAR(pgctx->statbar),
-			  pgctx->statbar_ctx_id);
-
-	/* Only first line can be displayed */
-	p = strchr(msg, '\n');
-	if (p)
-		*p = '\0';
-
-	gtk_statusbar_push(GTK_STATUSBAR(pgctx->statbar),
-			   pgctx->statbar_ctx_id, msg);
+	gtk_widget_set_visible(pgctx->info_bar, TRUE);
 
 	free(msg);
 	return 0;
 }
-#endif
 
 struct perf_error_ops perf_gtk_eops = {
 	.error		= perf_gtk__error,
-#ifdef HAVE_GTK_INFO_BAR_SUPPORT
 	.warning	= perf_gtk__warning_info_bar,
-#else
-	.warning	= perf_gtk__warning_statusbar,
-#endif
 };
diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c
index ff800047e697f..d887346c7a632 100644
--- a/tools/perf/ui/setup.c
+++ b/tools/perf/ui/setup.c
@@ -14,7 +14,7 @@ int use_browser = -1;
 
 #define PERF_GTK_DSO "libperf-gtk.so"
 
-#ifdef HAVE_GTK2_SUPPORT
+#ifdef HAVE_GTK4_SUPPORT
 
 static int setup_gtk_browser(void)
 {
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index df70e95a84704..123b6d5fcea78 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -145,6 +145,17 @@ static int annotated_source__alloc_histograms(struct annotated_source *src,
 	return src->histograms ? 0 : -1;
 }
 
+struct sym_hist_entry *
+annotated_source__hist_entry(struct annotated_source *src, const struct evsel *evsel, u64 offset)
+{
+	struct sym_hist_entry *entry;
+	long key = offset << 16 | evsel->core.idx;
+
+	if (!hashmap__find(src->samples, key, &entry))
+		return NULL;
+	return entry;
+}
+
 void symbol__annotate_zero_histograms(struct symbol *sym)
 {
 	struct annotation *notes = symbol__annotation(sym);
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index fa08d09b80f76..40038a3779d46 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -406,16 +406,8 @@ static inline struct sym_hist *annotation__histogram(struct annotation *notes,
 	return annotated_source__histogram(notes->src, evsel);
 }
 
-static inline struct sym_hist_entry *
-annotated_source__hist_entry(struct annotated_source *src, const struct evsel *evsel, u64 offset)
-{
-	struct sym_hist_entry *entry;
-	long key = offset << 16 | evsel->core.idx;
-
-	if (!hashmap__find(src->samples, key, &entry))
-		return NULL;
-	return entry;
-}
+struct sym_hist_entry *
+annotated_source__hist_entry(struct annotated_source *src, const struct evsel *evsel, u64 offset);
 
 static inline struct annotation *symbol__annotation(struct symbol *sym)
 {

3/2 2026/09/07 02:26 llm patch-evaluator
1m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 16405
							output: 101
							thoughts: 980
						

Thoughts:
**Patch Evaluation for Syzkaller Fuzzing**

Okay, so they want me to assess a patch series for syzkaller fuzzing. Let's get right to it. First, I need to see what's actually changed. I see that the patch touches a bunch of files: `tools/build/Makefile.feature`, `tools/build/feature/Makefile`, some `.c` files in `tools/build/feature/`, changes to `tools/perf/` related to documentation, makefiles, and a good chunk of GTK-related UI code. Looks like a big chunk of this is related to a GTK upgrade for the `perf` tool.

Given my understanding of how syzkaller works, I need to check against the defined criteria. Specifically, the negative criteria, the times when I would *not* consider it worth fuzzing. I'm looking for any modifications that fall into these categories: documentation, user-space code that doesn't affect the kernel image, decorative logging, or build system changes that don't alter compiled C logic.

Hmm. All the modified files are under `tools/`, specifically `tools/perf/` and `tools/build/`. It looks like this is a pure user-space change, the `perf` tool, which appears to be upgrading its GTK version. None of these changes seem to touch `vmlinux` or any kernel modules. Excellent! This *should* be straightforward.

Now, let me double-check. Just to be absolutely sure, I'll go through the list of modified files again, visually this time, to confirm that no non-`tools/` files were touched: `tools/build/Makefile.feature`, `tools/build/feature/Makefile`, and so on, all the way down to `tools/perf/util/annotate.h`. Yup, still all inside `tools/`. No red flags.

Therefore, applying the "WHEN TO RETURN WorthFuzzing=false" criteria, the verdict is clear: `WorthFuzzing = false`. There are no specific kernel modules affected, so the changes in these tools are irrelevant. `EnableConfigs` would be empty, and I don't see any `FocusSymbols` to flag.

Alright, I'm confident. I'll go ahead and set the results accordingly.



4/2 2026/09/07 02:27 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch series modifies only user-space tooling and build scripts under tools/ (specifically porting the perf GTK UI from GTK2 to GTK4 and minor helper relocation in tools/perf/util/annotate.c). It does not alter any kernel-space code, the compiled kernel image (vmlinux), or kernel modules.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch series modifies only user-space tooling and build scripts under tools/ (specifically porting the perf GTK UI from GTK2 to GTK4 and minor helper relocation in tools/perf/util/annotate.c). It does not alter any kernel-space code, the compiled kernel image (vmlinux), or kernel modules.",
  "WorthFuzzing": false
}

5/1 2026/09/07 02:27 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)