From nobody Fri Sep 25 20:02:09 2026 Received: from mail-yw1-f171.google.com (mail-yw1-f171.google.com [209.85.128.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D63BC337BB8 for ; Wed, 9 Sep 2026 02:50:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788922246; cv=none; b=kB0mJf5U2BKYFscGnU7srJOTQyat4xE5uOwkzZbSX5FN62b+MXjAaMoH42MPZm23bKJ0jbhnXp0ORdrFIbAzyeFJAZsQbJZkmmdac25FvRpJvBybdYmAtIYL6P1+xwPd1sOwb1oUwd7m5rJ4ErCVP/qwQG5ahp/63q0PszuGcOI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788922246; c=relaxed/simple; bh=UiI5a1w69pq486YFF3CYIk0RqR4PZnnB6GRz2MyTcxA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Hac2FXy1B+p46gDTG/0B1J+7F0xE5S9v9YqaMWOO91JKyYk1e8G51cVLy1+6Pwn8uRzA/oaMPL26SIVOZxSoEUDq1OHA85Ujr0rVMi9qLsdtdcH7qYVJkw8O05yYHwg3Xvn9FwS5Y5i6mjgThb5vHt3/kpHX1CK+5JzLXVbkH54= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=re7ZSYCL; arc=none smtp.client-ip=209.85.128.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="re7ZSYCL" Received: by mail-yw1-f171.google.com with SMTP id 00721157ae682-867a943b149so85090017b3.1 for ; Tue, 08 Sep 2026 19:50:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788922238; x=1789527038; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=nM4nO09OqQzGUxmO54c6Y6FRbsn4VH2OOYaXU9vXtRg=; b=re7ZSYCLxih1XVNdpn/TJ+EFbEfbS/EEux7Av1JSIxX2A0xJcF3VlkFwyBsb6nn2Gk w4w2FjFf4Tugd6LMTXOmMrZLfWAV3Mqsx5ZsDgbts5YRmg+P4oCk6gf3vDT+p8UH9K19 2fUpZegMaohsE4EZNyj/lFv5AUxxMfQ0kH3WIUbkdqeXbsIB7e4+FxgrOY2DCGoHfplq S0nauTTy0zzSpvcdm0CxjzrtJKmGS8x1w3pVGLIbCYGHabWksAXWFznBYTJf3lXjn7Xe qCIVtfE3L8onGnaKPBQ/vrAlOPDFDs7/2P4ZCA17qKtQth8Sy8jVwsYTrrPDpF9s9J5j LhRQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788922238; x=1789527038; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=nM4nO09OqQzGUxmO54c6Y6FRbsn4VH2OOYaXU9vXtRg=; b=TFc1YfvGhN24ovs1/Sg5omK9cGjDmLaAqN4Ic5MbeihHy8ihudq85/W/6KXy3p+aKC MKGM8gXAjIk2OSgePTYkUIGQpSO1rFKlc2kmO1twB0045E+9To3QZZIJERf+TYiGgJJd wR/JLpttOgurc/52y+Kouuv5j+15IUuTk0DR/KIaPKCvFhmp30dd4nyfJ9n60APuMRHJ KlnKFBPDho+0zG66BG+SUq51FjgX8jCpvYgVOf+fbfXHoa0a/wzI6euFufR0DuCJ713f /QwVOHZ4dw7V5AvHyd2mYFfH1JWQmyQVEHQjSPpOdEb+AckSe6bUgTKiQKx/IevGPRid cyjw== X-Gm-Message-State: AFuF++nAzU33sw3fUl0jIrCvEDzdvkkS9xULNXRvTprfcND3qmv6X9tI yAICDMkHaRPiGakh8waY+b4aE+c1mowcL8ZsUdBXLAqvZFF0l+ymOhEX X-Gm-Gg: AYBFou0ozP07vhxLQz8c0m38diK12IBq8yP0+WjoaH6mQ+edMKBfoVzOTfb88fY+bDa 7HP4m2f+xBfAZ0DLTxx4ffEhFWNZ01WUKcg2NCPeselQ0yp5UlVWSJ41OMk7nRDCMk1LEKdjTZj sqEcl1TkJ0y2PDE3DHnmdVTxAwQmUz4mozpF4ARNH8ZR51Z9GDqzGIQUeh9nlkNHjsR5M614SG4 xGCbLCEF5Sp0km2vK1fnX3xebYpAcn2ApDxVKP1Yp4XO//ylJfeVJTV8DaILthDDApRw7Jmc0SB ITW6Blah53kFrocFJC+all+AW61kNfftlPqwXS9J/MRYqDvZSc9rfJu6AleVJ4HHipvhQW6KAcm gimzeLiLe2BX/agguN0kqor6v9DDAT4Gd7h+gVzdCx4zqba34mc692OpEl48ReYB4xf0KF2AA+o Lu9h8RgGhh0hraku2EOwjUkQB1Zd355/Oz2ktbZoalJEhc3QkRn9znSkXMF7OsfBuYuDjErvo= X-Received: by 2002:a05:690e:d50:b0:671:476:fb32 with SMTP id 956f58d0204a3-6710476fbdemr1396482d50.90.1788922237832; Tue, 08 Sep 2026 19:50:37 -0700 (PDT) Received: from localhost ([2600:1702:7a90:6f9f:8bc4:8aec:108d:7a04]) by smtp.gmail.com with ESMTPSA id 00721157ae682-871439c454esm104051527b3.7.2026.09.08.19.50.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Sep 2026 19:50:36 -0700 (PDT) From: Matt Turner Date: Tue, 08 Sep 2026 22:50:34 -0400 Subject: [PATCH v8 1/3] tools: port perf ui from GTK 2 to GTK 4 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260908-perf-gtk2-v8-1-e90d5d155f0d@gmail.com> References: <20260908-perf-gtk2-v8-0-e90d5d155f0d@gmail.com> In-Reply-To: <20260908-perf-gtk2-v8-0-e90d5d155f0d@gmail.com> To: Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Alexander Shishkin , Jiri Olsa , Ian Rogers , Adrian Hunter , James Clark Cc: linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, Matt Turner X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=35384; i=mattst88@gmail.com; h=from:subject:message-id; bh=UiI5a1w69pq486YFF3CYIk0RqR4PZnnB6GRz2MyTcxA=; b=owGbwMvMwCW25rVmCc8sv+mMp9WSGLIWnKxMdH13cKdz0u359T9VndR9kl6a9Kqu39JUwVwzl UfPIIG74yMLgxgXw0wxRZa49Yoss9p2LPU5Lf0LZg4rE8gQaZEGBiBgYeDLTcwrNdIx0jPVNtQz BDJ0jOIhcnoMGpnFxaWpRbppBUUOefkliSWZ+XnFevkFqXkF6QV6aZlpJRkZ+UXFqUAj9PJSS0x dHd2MDA1MLB0tzJwsHE1NnJ2dDJ3cHB2dXZ2MLM1NDJwtHU1cLc0ZuDgFYK5RZmBkWF1YLnrq3K XIEtFT+/b+DxEq1vGxcuB3OOMxUdOkqqLxFsP/0L2cVxnueE1gYtp+a6F8/p6VsQ/Ph539Url/q ZnZz7gZTAA= X-Developer-Key: i=mattst88@gmail.com; a=openpgp; fpr=3BB639E56F861FA2E86505690FDD682D974CA72A Port straight to GTK 4 rather than GTK 3, since GTK 4 is where new development happens and GTK 3 is old itself now. GTK 4 drops GtkContainer, GdkScreen, and the gtk_main()/ gtk_dialog_run() family perf's GTK UI relied on. Containers get per-widget setters (gtk_box_append() and friends), monitor geometry comes from GdkMonitor instead of GdkScreen, and the main and error-dialog loops become explicit GMainLoops quit from the "close-request" and "response" signals. Widgets are visible by default now, so gtk_widget_show_all()/set_no_show_all() go away, and the remaining gtk_widget_show()/gtk_widget_hide() calls become gtk_widget_set_visible() (with a small wrapper where "response" needs to pass gtk_widget_hide() as a callback, since it no longer exists as a plain function). gtk_ui_progress__finish() skips destroying a progress dialog that was never created, since gtk_window_destroy() asserts on NULL where the old widget destroy tolerated it. Two spots the GTK 2 to GTK 3 port had missed (builtin-annotate.c, ui/gtk/setup.c still using HAVE_GTK2_SUPPORT and gtk_main_quit()) are fixed to match. Runtime fallout from the new signal-driven loops: the error dialog's nested loop hung if the parent window closed (GTK_DIALOG_DESTROY_WITH_PARENT destroys without emitting "response"); gtk_info_bar_get_content_area() is gone, breaking GTK_INFO_BAR_SUPPORT; the progress dialog's static widget pointers dangled after a manual close; perf_gtk__error() and the warning functions reused an exhausted va_list when vasprintf() failed. The error loop is tracked in a list instead of a single pointer, since perf_gtk__error() can be called re-entrantly (the dialog isn't modal) and a lone global leaked the outer loop when that happened. The list is only ever touched from the main thread: perf_gtk__error() updates it while handling a dialog, and SIGINT/SIGQUIT/SIGTERM are deferred to a GLib source via g_unix_signal_add() rather than calling perf_gtk__exit() straight out of a real signal handler, so quitting on those signals is serialized with the list update instead of racing it from signal-handler context. SIGSEGV/SIGFPE keep a real handler, since they're synchronous faults with no "later" to defer to, but it's pared down to reporting and reraising the default disposition (perf_gtk__fatal_signal()): there's no safe way to run GTK/GLib code from the faulting context. stdarg.h, stdio.h, and string.h are now included explicitly where used (util.c, hists.c, annotate.c) rather than relying on transitive includes, which musl doesn't guarantee. The gtk4-infobar feature check is dropped: GtkInfoBar has existed unconditionally since GTK 3.10, so the check can only ever pass, and it was failing outright here anyway since gtk_info_bar_new() is deprecated and the check treats deprecation warnings as errors. HAVE_GTK_INFO_BAR_SUPPORT and its statusbar-only fallback go away; the info bar is now built unconditionally. Signed-off-by: Matt Turner --- tools/build/Makefile.feature | 4 +- tools/build/feature/Makefile | 10 +-- tools/build/feature/test-gtk2-infobar.c | 12 --- tools/build/feature/{test-gtk2.c =3D> test-gtk4.c} | 4 +- tools/perf/Documentation/perf-report.txt | 2 +- tools/perf/Makefile | 2 +- tools/perf/Makefile.config | 22 +++--- tools/perf/Makefile.perf | 6 +- tools/perf/builtin-annotate.c | 8 +- tools/perf/builtin-report.c | 8 +- tools/perf/scripts/install-build-deps.sh | 4 +- tools/perf/tests/make | 4 +- tools/perf/ui/gtk/annotate.c | 36 ++++----- tools/perf/ui/gtk/browser.c | 96 ++++++++++++++++++--= --- tools/perf/ui/gtk/gtk.h | 16 ++-- tools/perf/ui/gtk/hists.c | 43 +++++------ tools/perf/ui/gtk/progress.c | 40 +++++++--- tools/perf/ui/gtk/setup.c | 5 +- tools/perf/ui/gtk/util.c | 97 ++++++++++++++------= ---- tools/perf/ui/setup.c | 2 +- 20 files changed, 239 insertions(+), 182 deletions(-) diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 99eb0ea09537..4ec95c35a5c1 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -113,8 +113,7 @@ FEATURE_TESTS_EXTRA :=3D \ compile-x32 \ cplus-demangle \ cxa-demangle \ - gtk2 \ - gtk2-infobar \ + gtk4 \ hello \ babeltrace2-ctf-writer \ libcapstone \ @@ -143,6 +142,7 @@ endif FEATURE_DISPLAY ?=3D \ libdw \ glibc \ + gtk4 \ libelf \ libnuma \ numa_num_possible_cpus \ diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index 7d165018116a..01c48e6ef021 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -11,8 +11,7 @@ FILES=3D \ 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 =20 -$(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) =20 grep-libs =3D $(filter -l%,$(1)) strip-libs =3D $(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 b1b716dd5733..000000000000 --- 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 -#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-gtk= 4.c similarity index 76% rename from tools/build/feature/test-gtk2.c rename to tools/build/feature/test-gtk4.c index 2aaf4bfc2055..b9520e7408b9 100644 --- a/tools/build/feature/test-gtk2.c +++ b/tools/build/feature/test-gtk4.c @@ -3,9 +3,9 @@ #include #pragma GCC diagnostic error "-Wstrict-prototypes" =20 -int main(int argc, char *argv[]) +int main(void) { - gtk_init(&argc, &argv); + gtk_init(); =20 return 0; } diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Document= ation/perf-report.txt index 22f87eaa3279..7af9b3f81c06 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. =20 ---gtk:: Use the GTK2 interface. +--gtk:: Use the GTK4 interface. =20 -k:: --vmlinux=3D:: diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 5b713837eede..56014106479a 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=3D1 -f tests/make REUSE_FEATURES_DUMP=3D1 MK=3DMakefile SET= _PARALLEL=3D1 --no-print-directory tarpkg make_static make_with_gtk2 out + @$(MAKE) SHUF=3D1 -f tests/make REUSE_FEATURES_DUMP=3D1 MK=3DMakefile SET= _PARALLEL=3D1 --no-print-directory tarpkg make_static make_with_gtk4 out =20 build-test-tarball: @$(MAKE) -f tests/make REUSE_FEATURES_DUMP=3D1 MK=3DMakefile SET_PARALLEL= =3D1 --no-print-directory out diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 4d5993da9f94..3e59e2b7eaec 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -774,20 +774,16 @@ ifndef NO_SLANG endif endif =20 -ifdef GTK2 - FLAGS_GTK2=3D$(CFLAGS) $(LDFLAGS) $(EXTLIBS) $(shell $(PKG_CONFIG) --lib= s --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-d= evel or libgtk2.0-dev) - NO_GTK2 :=3D 1 +ifdef GTK4 + FLAGS_GTK4=3D$(CFLAGS) $(LDFLAGS) $(EXTLIBS) $(shell $(PKG_CONFIG) --lib= s --cflags gtk4 2>/dev/null) + $(call feature_check,gtk4) + ifneq ($(feature-gtk4), 1) + $(warning GTK4 not found, disables GTK4 support. Please install gtk4-d= evel or libgtk-4-dev) + NO_GTK4 :=3D 1 else - $(call feature_check,gtk2-infobar) - ifeq ($(feature-gtk2-infobar), 1) - GTK_CFLAGS :=3D -DHAVE_GTK_INFO_BAR_SUPPORT - endif - CFLAGS +=3D -DHAVE_GTK2_SUPPORT - GTK_CFLAGS +=3D $(shell $(PKG_CONFIG) --cflags gtk+-2.0 2>/dev/null) - GTK_LIBS :=3D $(shell $(PKG_CONFIG) --libs gtk+-2.0 2>/dev/null) + CFLAGS +=3D -DHAVE_GTK4_SUPPORT + GTK_CFLAGS +=3D $(shell $(PKG_CONFIG) --cflags gtk4 2>/dev/null) + GTK_LIBS :=3D $(shell $(PKG_CONFIG) --libs gtk4 2>/dev/null) EXTLIBS +=3D -ldl endif endif diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 29cfd44c427f..2438b40eaaec 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 +=3D -I$(OUTPUT) endif =20 -ifdef GTK2 +ifdef GTK4 ALL_PROGRAMS +=3D $(OUTPUT)libperf-gtk.so GTK_IN :=3D $(OUTPUT)gtk-in.o endif @@ -811,7 +811,7 @@ check: prepare =20 ### Installation rules =20 -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 69cb72b2082a..15163e081a8c 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; =20 -#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 =3D 1; #endif -#ifdef HAVE_GTK2_SUPPORT +#ifdef HAVE_GTK4_SUPPORT else if (annotate.use_gtk) use_browser =3D 2; #endif diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 60d1f166629e..d14384c58466 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 =3D 1; #endif -#ifdef HAVE_GTK2_SUPPORT +#ifdef HAVE_GTK4_SUPPORT else if (report.use_gtk) use_browser =3D 2; #endif diff --git a/tools/perf/scripts/install-build-deps.sh b/tools/perf/scripts/= install-build-deps.sh index d003e7fab2be..a601a5260c17 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=3D1), + # builds and deprecated in favor of capstone, GTK4, LIBPERL and + # LIBUNWIND support (ifdef GTK4 / ifdef LIBPERL / LIBUNWIND=3D1), # 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 d2c2f526e1db..202ab5501916 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -95,7 +95,7 @@ make_no_babeltrace2 :=3D NO_BABELTRACE2=3D1 make_with_coresight :=3D CORESIGHT=3D1 make_no_sdt :=3D NO_SDT=3D1 make_no_libpfm4 :=3D NO_LIBPFM4=3D1 -make_with_gtk2 :=3D GTK2=3D1 +make_with_gtk4 :=3D GTK4=3D1 make_refcnt_check :=3D EXTRA_CFLAGS=3D"-DREFCNT_CHECKING=3D1" make_tags :=3D tags make_cscope :=3D cscope @@ -318,7 +318,7 @@ $(run): $(call test,$@) && \ rm -rf $@ $$TMP_DEST || (cat $@ ; false) =20 -make_with_gtk2: +make_with_gtk4: $(call clean) @TMP_DEST=3D$$(mktemp -d); \ cmd=3D"cd $(PERF) && $(MAKE_F) $($@) $(PARALLEL_OPT) $(O_OPT) DESTDIR=3D$= $TMP_DEST"; \ diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c index 8920e298420a..094dfa045b77 100644 --- a/tools/perf/ui/gtk/annotate.c +++ b/tools/perf/ui/gtk/annotate.c @@ -11,6 +11,7 @@ #include "ui/helpline.h" #include #include +#include =20 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); } =20 - gtk_container_add(GTK_CONTAINER(window), view); + gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(window), view); =20 list_for_each_entry_safe(pos, n, ¬es->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; =20 - 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(); =20 - window =3D gtk_window_new(GTK_WINDOW_TOPLEVEL); + window =3D gtk_window_new(); gtk_window_set_title(GTK_WINDOW(window), "perf annotate"); =20 - g_signal_connect(window, "delete_event", gtk_main_quit, NULL); - pgctx =3D perf_gtk__activate_context(window); if (!pgctx) return -1; =20 - vbox =3D gtk_vbox_new(FALSE, 0); + vbox =3D gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); notebook =3D gtk_notebook_new(); pgctx->notebook =3D notebook; =20 - gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); + gtk_widget_set_vexpand(notebook, TRUE); + gtk_box_append(GTK_BOX(vbox), notebook); =20 infobar =3D 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); =20 statbar =3D perf_gtk__setup_statusbar(); - gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0); + gtk_box_append(GTK_BOX(vbox), statbar); =20 - gtk_container_add(GTK_CONTAINER(window), vbox); + gtk_window_set_child(GTK_WINDOW(window), vbox); } =20 - scrolled_window =3D gtk_scrolled_window_new(NULL, NULL); + scrolled_window =3D gtk_scrolled_window_new(); tab_label =3D gtk_label_new(sym->name); =20 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), @@ -267,12 +262,11 @@ void perf_gtk__show_annotations(void) return; =20 window =3D pgctx->main_window; - gtk_widget_show_all(window); =20 perf_gtk__resize_window(window); - gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_widget_set_visible(window, TRUE); =20 - gtk_main(); + perf_gtk__run_main_loop(window); =20 perf_gtk__deactivate_context(&pgctx); } diff --git a/tools/perf/ui/gtk/browser.c b/tools/perf/ui/gtk/browser.c index d2dadf3873fb..98f1d6bfcb24 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" =20 +#include #include =20 -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), inste= ad + * 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); } =20 void perf_gtk__resize_window(GtkWidget *window) { GdkRectangle rect; - GdkScreen *screen; - int monitor; + GdkMonitor *monitor; + GdkDisplay *display; + GListModel *monitors; int height; int width; =20 - screen =3D gtk_widget_get_screen(window); + display =3D gtk_widget_get_display(window); + monitors =3D gdk_display_get_monitors(display); + monitor =3D g_list_model_get_item(monitors, 0); + if (!monitor) { + gtk_window_set_default_size(GTK_WINDOW(window), 800, 600); + return; + } =20 - monitor =3D 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); =20 width =3D rect.width * 3 / 4; height =3D rect.height * 3 / 4; =20 - 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 =3D 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); } =20 const char *perf_gtk__get_percent_color(double percent) @@ -42,33 +102,35 @@ const char *perf_gtk__get_percent_color(double percent) return NULL; } =20 -#ifdef HAVE_GTK_INFO_BAR_SUPPORT +static void perf_gtk__hide_widget(GtkWidget *widget, gint response_id __ma= ybe_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; =20 info_bar =3D gtk_info_bar_new(); - gtk_widget_set_no_show_all(info_bar, TRUE); + gtk_widget_set_visible(info_bar, FALSE); =20 label =3D gtk_label_new(""); - gtk_widget_show(label); + gtk_widget_set_visible(label, TRUE); =20 - content_area =3D 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); =20 - 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); =20 pgctx->info_bar =3D info_bar; pgctx->message_label =3D label; =20 return info_bar; } -#endif =20 GtkWidget *perf_gtk__setup_statusbar(void) { diff --git a/tools/perf/ui/gtk/gtk.h b/tools/perf/ui/gtk/gtk.h index a2b497f03fd6..6b37fd9943c9 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; =20 -#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); =20 -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 =20 struct evsel; struct evlist; diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index bae21f336ae6..716dcf02bd0e 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 +#include +#include #include #include =20 @@ -395,11 +397,9 @@ static void perf_gtk__show_hists(GtkWidget *window, st= ruct hists *hists, } } =20 - 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); } =20 static void perf_gtk__add_hierarchy_entries(struct hists *hists, @@ -583,11 +583,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); =20 - 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); } =20 int evlist__gtk_browse_hists(struct evlist *evlist, const char *help, @@ -600,36 +598,32 @@ int evlist__gtk_browse_hists(struct evlist *evlist, c= onst char *help, GtkWidget *statbar; GtkWidget *window; =20 - 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(); =20 - window =3D gtk_window_new(GTK_WINDOW_TOPLEVEL); + window =3D gtk_window_new(); =20 gtk_window_set_title(GTK_WINDOW(window), "perf report"); =20 - g_signal_connect(window, "delete_event", gtk_main_quit, NULL); - pgctx =3D perf_gtk__activate_context(window); if (!pgctx) return -1; =20 - vbox =3D gtk_vbox_new(FALSE, 0); + vbox =3D gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); =20 notebook =3D gtk_notebook_new(); =20 - gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); + gtk_widget_set_vexpand(notebook, TRUE); + gtk_box_append(GTK_BOX(vbox), notebook); =20 info_bar =3D 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); =20 statbar =3D perf_gtk__setup_statusbar(); - gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0); + gtk_box_append(GTK_BOX(vbox), statbar); =20 - gtk_container_add(GTK_CONTAINER(window), vbox); + gtk_window_set_child(GTK_WINDOW(window), vbox); =20 evlist__for_each_entry(evlist, pos) { struct hists *hists =3D evsel__hists(pos); @@ -649,7 +643,7 @@ int evlist__gtk_browse_hists(struct evlist *evlist, con= st char *help, } } =20 - scrolled_window =3D gtk_scrolled_window_new(NULL, NULL); + scrolled_window =3D gtk_scrolled_window_new(); =20 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, @@ -665,15 +659,12 @@ int evlist__gtk_browse_hists(struct evlist *evlist, c= onst char *help, gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_la= bel); } =20 - 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); =20 ui_helpline__push(help); =20 - gtk_main(); + perf_gtk__run_main_loop(window); =20 perf_gtk__deactivate_context(&pgctx); =20 diff --git a/tools/perf/ui/gtk/progress.c b/tools/perf/ui/gtk/progress.c index eea6fcde518a..770f9251b54b 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 +#include =20 #include "gtk.h" #include "../progress.h" +#include =20 static GtkWidget *dialog; static GtkWidget *progress; =20 +static void gtk_ui_progress__destroyed(GtkWidget *widget __maybe_unused, + gpointer data __maybe_unused) +{ + dialog =3D NULL; + progress =3D NULL; +} + static void gtk_ui_progress__update(struct ui_progress *p) { double fraction =3D p->total ? 1.0 * p->curr / p->total : 0.0; char buf[1024]; =20 if (dialog =3D=3D NULL) { - GtkWidget *vbox =3D gtk_vbox_new(TRUE, 5); + GtkWidget *vbox =3D gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); GtkWidget *label =3D gtk_label_new(p->title); =20 - dialog =3D gtk_window_new(GTK_WINDOW_TOPLEVEL); + dialog =3D gtk_window_new(); progress =3D gtk_progress_bar_new(); =20 - 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); =20 - gtk_container_add(GTK_CONTAINER(dialog), vbox); + gtk_window_set_child(GTK_WINDOW(dialog), vbox); + + g_signal_connect(dialog, "destroy", + G_CALLBACK(gtk_ui_progress__destroyed), NULL); =20 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); =20 - gtk_widget_show_all(dialog); + gtk_widget_set_visible(dialog, TRUE); } =20 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); =20 - /* 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); } =20 static void gtk_ui_progress__finish(void) { + if (dialog =3D=3D NULL) + return; + /* this will also destroy all of its children */ - gtk_widget_destroy(dialog); + gtk_window_destroy(GTK_WINDOW(dialog)); =20 dialog =3D NULL; } diff --git a/tools/perf/ui/gtk/setup.c b/tools/perf/ui/gtk/setup.c index f5eee4d66873..9b44f3719747 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(); =20 - return gtk_init_check(NULL, NULL) ? 0 : -1; + return gtk_init_check() ? 0 : -1; } =20 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 c47f5c387838..5823ff0d7c2a 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" =20 +#include +#include #include -#include +#include #include =20 struct perf_gtk_context *pgctx; @@ -28,86 +30,103 @@ int perf_gtk__deactivate_context(struct perf_gtk_conte= xt **ctx) return 0; } =20 +/* + * 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; =20 + 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); =20 dialog =3D gtk_message_dialog_new_with_markup(GTK_WINDOW(pgctx->main_wind= ow), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Error\n\n%s", msg); - gtk_dialog_run(GTK_DIALOG(dialog)); =20 - 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 =3D g_main_loop_new(NULL, FALSE); + perf_gtk__error_loops =3D 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 =3D g_slist_remove(perf_gtk__error_loops, loop); + g_main_loop_unref(loop); + free(msg); return 0; } =20 -#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; =20 + 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); =20 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); + gtk_widget_set_visible(pgctx->info_bar, TRUE); =20 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 =3D strchr(msg, '\n'); - if (p) - *p =3D '\0'; - - gtk_statusbar_push(GTK_STATUSBAR(pgctx->statbar), - pgctx->statbar_ctx_id, msg); - - free(msg); - return 0; -} -#endif =20 struct perf_error_ops perf_gtk_eops =3D { .error =3D perf_gtk__error, -#ifdef HAVE_GTK_INFO_BAR_SUPPORT .warning =3D perf_gtk__warning_info_bar, -#else - .warning =3D perf_gtk__warning_statusbar, -#endif }; diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c index ff800047e697..d887346c7a63 100644 --- a/tools/perf/ui/setup.c +++ b/tools/perf/ui/setup.c @@ -14,7 +14,7 @@ int use_browser =3D -1; =20 #define PERF_GTK_DSO "libperf-gtk.so" =20 -#ifdef HAVE_GTK2_SUPPORT +#ifdef HAVE_GTK4_SUPPORT =20 static int setup_gtk_browser(void) { --=20 2.54.0 From nobody Fri Sep 25 20:02:09 2026 Received: from mail-yw1-f171.google.com (mail-yw1-f171.google.com [209.85.128.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 33C9633D4E4 for ; Wed, 9 Sep 2026 02:50:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788922247; cv=none; b=Oc/+HjvPjhPKHroygJy+fQu3eofLRDMv1ZDh6jr83Zd3wg2jN3ju/DSABNzNO8xSI1jtL7pBVfR+F1e97nxizjXkJEUrUgQh7Kc97+5vLj7KDy2kynNfizly9ivAYrT3kGBS0bpcvD00d4pfqhR6Vp8SXRMr3OdrIIVmX5zaCac= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788922247; c=relaxed/simple; bh=V13NjxcZ8Y+EH56zGxBFcHbqNp/dD0O+9buYDXRen/c=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=FBjlsI0ko6D+OUvOaUwVY1ycig5MFtjxSw8/CTMt0r/P7tjP7Cx4w0YlcnrJppztGt6NpVYTxLHAOxLCIZ9LDLm5J50GBnR4cmZsyyJ/Krphd952DG2oKrrANh0pd+YFwkpWHRO7OT+MQFuCtArnndDuXCmS7/BDbBtCtIL3fGM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=d3zN6fcL; arc=none smtp.client-ip=209.85.128.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="d3zN6fcL" Received: by mail-yw1-f171.google.com with SMTP id 00721157ae682-866e57f63a3so61297067b3.3 for ; Tue, 08 Sep 2026 19:50:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788922239; x=1789527039; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=N8uHZ58VC3qmxtEe8gO2hHAyHIZcWIh7nanbyJ3N2VQ=; b=d3zN6fcLsnGo3uyLts311miytLAfQGFYdFssbgU5ZapMtJVdS5k3BCqjQkl/iiNmHF ZCcslMxNABhTG3fDw97LDMKg76AMbRLBK4t5IjKj5FeAEd7sNsPuLQgo9ZANjzXnYKVE 6XBOHCSFQeWqSpYleaj9qGCCH0CA0trW012U+bSOqSC3ikvc5YFm7VOnlLIzDQ56zcLc 0JxtG+CLwJB1dsEB/sRtVqu+Tg91MviOAU7cf/GMvPK4HOzrJTQ0gcna8QyOXffk/lUa 8z4nyfADhEjc/Hs2UfA4gsj0ZWkniQmFOy1yNci8R8NYdikVPrq9bGQVuMRxuRuY0SUk a4Uw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788922240; x=1789527040; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=N8uHZ58VC3qmxtEe8gO2hHAyHIZcWIh7nanbyJ3N2VQ=; b=KFt0uWbUSCw2Q0XUQBT+YdAysJjr5Fdzb5LB/lqGkICgCzYkK79B0J1hEY3ICoZUlL Rr08yK2BiT7mI0tVu7XxvMsjzpd627ebV811QANHu1PL4yLSDP3VXYGsZftCQE2ljTV4 z7eO3onzBeh3+rgwdqgKJH7kGVsip1W6IBa6AVLi6uF/UJr//nuUzJPr9Vhx0xt80NDg HoIEKb1WFbccjgRDXk+qx17JbzdniSjW84vItx4bDAEBT3uv9AriTQFG+LW86PBqW9jU aQKswVFpjKHXPViQftf7wX9ym4FBaFRSxk8pi2y10k0+3TC2BAGHil24stSkLaKJPxRs vLfg== X-Gm-Message-State: AFuF++nSQJASnzg+KK64zBDOlZqR3HDYUr0ZfSvRx97ckDnFqspCaZCW 41GOXqdKAIrr0ysX4uKLxHal3LX8Aro8B1eoYLPv6JETQS/7351qBaaf X-Gm-Gg: AYBFou3hsp1lzngj/tXP/ZI8XfsjeVnnTglK0IXfkg9+7te0pCDkM9OR176aODBdqdm pg5bvACvgzx8a0l+KVrIXfhs9ckwM+ntrqiJQPD1QjCV87n1cLRCs+rRSOE2BgV2XmIg//iIvqv XxtLKJdm/IYuQpbt6OOhrDXZ9KY9Q2xbAc7hO4Yy7KQ8WVhl7PYpQjpZ+X4OpPsTYzu8dVSA+Ir 5oNTsEIQn7/kF6087nav0mwhjpYjfcOPmHVYRrQCT7yG6G8+x/xaTaJn0PdXxoRxPFGrnbkl1B5 akV06TVU1z1Mii/kOIH/MWQXwZv/CN4LX4qdygIVjyMn8UVX4KrTO9kjXDio7oRSn80Cuj5PZ0l QWYW8IbyT4coiFNIJfA8yE7rlp8ZeP+jv67Q527z4tMgv2w35LisxdF7PxCwC7mKJ1cFuppAlc7 HpTNcm4ZBg9yVFUSIRPy9qCCu8vOfkWyv3U7t2BP+gy6qiDGYQ5ocjeUy8yY6pXTvffA9fL/A= X-Received: by 2002:a05:690c:6d81:b0:87a:ea5c:c21e with SMTP id 00721157ae682-87aea5cc507mr53889867b3.21.1788922239624; Tue, 08 Sep 2026 19:50:39 -0700 (PDT) Received: from localhost ([2600:1702:7a90:6f9f:8bc4:8aec:108d:7a04]) by smtp.gmail.com with ESMTPSA id 00721157ae682-87149316356sm103354187b3.9.2026.09.08.19.50.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Sep 2026 19:50:38 -0700 (PDT) From: Matt Turner Date: Tue, 08 Sep 2026 22:50:35 -0400 Subject: [PATCH v8 2/3] perf tools: make the GTK4 report browser actually loadable at runtime Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260908-perf-gtk2-v8-2-e90d5d155f0d@gmail.com> References: <20260908-perf-gtk2-v8-0-e90d5d155f0d@gmail.com> In-Reply-To: <20260908-perf-gtk2-v8-0-e90d5d155f0d@gmail.com> To: Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Alexander Shishkin , Jiri Olsa , Ian Rogers , Adrian Hunter , James Clark Cc: linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, Matt Turner X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=3810; i=mattst88@gmail.com; h=from:subject:message-id; bh=V13NjxcZ8Y+EH56zGxBFcHbqNp/dD0O+9buYDXRen/c=; b=owGbwMvMwCW25rVmCc8sv+mMp9WSGLIWnKw0Tw9puPqzZmJTvEKyzNTcmV/mKz+aKCptcahRd 9Li54GLOj6yMIhxMcwUU2SJW6/IMqttx1Kf09K/YOawMoEMkRZpYAACFga+3MS8UiMdIz1TbUM9 QyBDxygeIqfHoJFZXFyaWqSbVlDkkJdfkliSmZ9XrJdfkJpXkF6gl5aZVpKRkV9UnAo0Qi8vtcT U1dHNyNDAxNLRwszJwtHUxNnZydDJzdHR2dXJyNLcxMDZ0tHE1dKcgYtTAOYa1xxGhmO5Bffjb3 yq/LaUNeil2E2Z8uSnRdXzTA705Lj+W6V+U46R4cblB2H+9+Xmd69a+j3cvu70sp/7DDkial0fR PN5Jz89wQAA X-Developer-Key: i=mattst88@gmail.com; a=openpgp; fpr=3BB639E56F861FA2E86505690FDD682D974CA72A perf report --gtk dlopen()s libperf-gtk.so, which expects to resolve symbols back against the running perf binary (callchain_param, symbol_conf, evsel__name, and friends live in perf, not the plugin). Two things broke that after the GTK 4 port: perf never passed -rdynamic, so none of its symbols were in its dynamic symbol table for a dlopen()ed plugin to find. Add -rdynamic to LDFLAGS when GTK4 support is enabled. annotated_source__hist_entry() was a static inline in annotate.h, so ui/gtk/annotate.c calling it pulled hashmap__find()'s expansion, hashmap_find(), into libperf-gtk.so as an undefined symbol. The only hashmap_find perf links against normally is libbpf's internal one (tools/lib/bpf/hashmap.c), built with -fvisibility=3Dhidden, so it can never be exported to a dlopen()ed plugin regardless of LDFLAGS. Move annotated_source__hist_entry() into annotate.c as an ordinary exported function, so the plugin depends on it the same way it already depends on evsel__group_desc() and friends. With both fixes, a default 'make GTK4=3D1' build (libbpf statically linked) can dlopen() libperf-gtk.so and open the report browser without NO_LIBBPF=3D1 or manual LDFLAGS. Verified with perf report --gtk against real perf.data on a GTK4 desktop. Signed-off-by: Matt Turner --- tools/perf/Makefile.config | 5 +++++ tools/perf/util/annotate.c | 11 +++++++++++ tools/perf/util/annotate.h | 12 ++---------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 3e59e2b7eaec..4ee7393a39f9 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -785,6 +785,11 @@ ifdef GTK4 GTK_CFLAGS +=3D $(shell $(PKG_CONFIG) --cflags gtk4 2>/dev/null) GTK_LIBS :=3D $(shell $(PKG_CONFIG) --libs gtk4 2>/dev/null) EXTLIBS +=3D -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 +=3D -rdynamic endif endif =20 diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index df70e95a8470..123b6d5fcea7 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -145,6 +145,17 @@ static int annotated_source__alloc_histograms(struct a= nnotated_source *src, return src->histograms ? 0 : -1; } =20 +struct sym_hist_entry * +annotated_source__hist_entry(struct annotated_source *src, const struct ev= sel *evsel, u64 offset) +{ + struct sym_hist_entry *entry; + long key =3D 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 =3D symbol__annotation(sym); diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index fa08d09b80f7..40038a3779d4 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -406,16 +406,8 @@ static inline struct sym_hist *annotation__histogram(s= truct annotation *notes, return annotated_source__histogram(notes->src, evsel); } =20 -static inline struct sym_hist_entry * -annotated_source__hist_entry(struct annotated_source *src, const struct ev= sel *evsel, u64 offset) -{ - struct sym_hist_entry *entry; - long key =3D 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 ev= sel *evsel, u64 offset); =20 static inline struct annotation *symbol__annotation(struct symbol *sym) { --=20 2.54.0 From nobody Fri Sep 25 20:02:09 2026 Received: from mail-yw1-f175.google.com (mail-yw1-f175.google.com [209.85.128.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3AB8B32470F for ; Wed, 9 Sep 2026 02:50:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788922250; cv=none; b=VYLSERbRue3ZqHk3UBHfqhgIKXAVfa0nLDTgmPjeK6K6nPm8GaFRKeDDy9NdgdpyeoizfajPWEIl8oxMiP5j91miJSOF1AVr+r/ragMXL5WpIT6q/6qoU2APVZ7cZL0+prqN2HSXY7Dg3G0hdq5QHInEEoE8Xp59BZn2BB7IxBo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788922250; c=relaxed/simple; bh=Jor5oXgeuFFmM9DJt4tFFBEGt97G7Zs2U7XOztWKBic=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=l8GFSGG8aE3TjqlSX+v1PRF4wJ9WI+r60EOPSUVh49r2l/fq20YHS/jk2yH7WCgPTiDazfXusX1g2M88Wt0UBKAE3EMGU2BUOUDhNsCLwd8aTIu6zziSwnJHCAZ7AL23fFGAwAS2Sg7ZrUsXqXG0TE6lujwNRsmWBEPnhV50YWA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=U9o/zsnR; arc=none smtp.client-ip=209.85.128.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="U9o/zsnR" Received: by mail-yw1-f175.google.com with SMTP id 00721157ae682-85aa9c1308dso43174017b3.3 for ; Tue, 08 Sep 2026 19:50:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788922243; x=1789527043; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=XNYcnB2WxFeBr7ZY16EYpHfgp99fE4ngetuj/d9sZ7o=; b=U9o/zsnRIS7SCjOq/dpU1KRO2hr+5piGvO0oW4quatlg0WoaZNSjB6jbJbMkyFvEPO 9Ei6X+NA2mxXRxSWpN6HTh0oTI0Rw3sDt7/ixBiOweL6yNx/nfR5cYy64JKg7Qh/8mvp CeOhBozaaDiw7OFFjBczgmhQXS4F1v98YkvtPEqaH1PvsJiLJ39TAYMJf5FbDTnt6eK9 TIoQOo9MWh/+I/4LqJacEgYY/O5mmhg4hG3ytrhP18+oGV/TyEhEJFU3b4qkDDwxHx5y yVuW+6epVw+L1x2MGa1ycGMwjwyreU1uQMMzTTn1zRswR8p/VjLR3qm/vx4jH+3Fbrgk 7JOQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788922243; x=1789527043; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=XNYcnB2WxFeBr7ZY16EYpHfgp99fE4ngetuj/d9sZ7o=; b=r4kTHYFoIT+1wfYSz3FZpTKOEE7I1XpvkWkjnKVdI6BtGVHQweIL94ID+jUc1ib/fB 8UpMyb6TOticiILbPoTUjsGQZ17/FcFMD4mmv+jb4mwIQSWlsNaXz3QRe9J65zwJKnW1 yT3r0AAEW8gnDhXeHd4xU0mtA3abVbo2oV3EwpkI9Raw/fyx3NK780BJqnxjvzEYAWmg TB9+P2Zg0Hd4bIhp7iSperF+Htzeetf6cEjLVJt2+AHmGQJvqMmxnrLqR8oGWyBTLU6N 1bqypGaLvuXxX5//PJNPI1JuZSo2uLUYSGEjwe6h/8EEDlZAeOKkEzh9GX/host/XQYa h+vQ== X-Gm-Message-State: AFuF++k2Uw1iqoykpkh4ytL9RM4dJK3i0Kx9a5mKrsenH1UYksiE/kaw ABDluaMwfJfR7CIJ1z/mAgumz30mYqepZb4G/68k96Jv7lHTRKE/BXwo X-Gm-Gg: AYBFou3Th860AK8MRqUHxdPiPlpQrNjN7AqSqqJciDZK7+le1x2Us8cCCE9kH0n4BP2 oBJvbHd464SUndKWCTNZVZU6tGCZz0cR/ZRzFMUGVImWQb1iBqaBK6+IxIb7I0rEjlOpABLOBqM HCJ5dBWapO0VYEwUI0aEcBRRdvKAkmZGPrXB1nQaJbDSANAzMBCasdmTleHjiow3ZB6nbca28vn bHtLzvGA3GQf5xgVSOzNVSLarKoTlH9KQGiSpq47qoNFyIt2kIVtScm0VAyHAdw8LQtPoaQoUN0 ttl6uP03mvKYwbcz35ew5kim9T8sm6SCXhrOvtV6lp9vArGqwGgbBVG6gw7GUxPr5sdYH3pECk+ ZlHIIrnRz6tsOeDjMXkV6Cj02YRlTK119+rpShRWvmpJOE74q1QaMbi/0EnxkwcxYWLRVa1w7I4 FbHaJDLqIynZJykEHYUlm9wq4wVCbSITV2btb1wCMNtNKBIgtb6qKgjEHNC4fUCM/jKkAEVcw= X-Received: by 2002:a05:690c:e653:b0:836:ec3d:b5e6 with SMTP id 00721157ae682-8712a9ea11amr97294587b3.28.1788922242884; Tue, 08 Sep 2026 19:50:42 -0700 (PDT) Received: from localhost ([2600:1702:7a90:6f9f:8bc4:8aec:108d:7a04]) by smtp.gmail.com with ESMTPSA id 00721157ae682-8714bb0efc1sm103407387b3.45.2026.09.08.19.50.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Sep 2026 19:50:40 -0700 (PDT) From: Matt Turner Date: Tue, 08 Sep 2026 22:50:36 -0400 Subject: [PATCH v8 3/3] perf tools gtk: fix two hierarchy-view stack buffer overflows Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260908-perf-gtk2-v8-3-e90d5d155f0d@gmail.com> References: <20260908-perf-gtk2-v8-0-e90d5d155f0d@gmail.com> In-Reply-To: <20260908-perf-gtk2-v8-0-e90d5d155f0d@gmail.com> To: Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Alexander Shishkin , Jiri Olsa , Ian Rogers , Adrian Hunter , James Clark Cc: linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, Matt Turner X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=4405; i=mattst88@gmail.com; h=from:subject:message-id; bh=Jor5oXgeuFFmM9DJt4tFFBEGt97G7Zs2U7XOztWKBic=; b=owGbwMvMwCW25rVmCc8sv+mMp9WSGLIWnKxMDHS7uH7V3Gqnmbe1LwVavFsj+GfHV9aCnjWXG +e6dfmqdXxkYRDjYpgppsgSt16RZVbbjqU+p6V/wcxhZQIZIi3SwAAELAx8uYl5pUY6Rnqm2oZ6 hkCGjlE8RE6PQSOzuLg0tUg3raDIIS+/JLEkMz+vWC+/IDWvIL1ALy0zrSQjI7+oOBVohF5eaom pq6ObkaGBiaWjhZmThaOpibOzk6GTm6Ojs6uTkaW5iYGzpaOJq6U5AxenAMw1ClUM/4MN5pnu/S 1V3LnqF5eIYq2KutSc1vjI+uUNL3qezz6l28Pw38lhY5d11J8G+SKz04vtF2v/NFzoL9z6bc+51 tJlj/z42QA= X-Developer-Key: i=mattst88@gmail.com; a=openpgp; fpr=3BB639E56F861FA2E86505690FDD682D974CA72A perf_gtk__show_hierarchy() builds a merged column header for the hierarchy view with unbounded strcat() calls into a 512-byte stack buffer. The pieces being appended come from tracepoint field names and sort-key headers in perf.data, so a file with enough dynamic sort keys or long enough field names overflows the buffer. perf_gtk__add_hierarchy_entries() has a related bug in the loop that formats each entry's value columns. fmt->entry()/fmt->color() return via scnprintf(), so ret is clamped to at most hpp->size - 1, but advance_hpp(hpp, ret + 2) doesn't clamp: when ret hits that maximum, ret + 2 exceeds hpp->size by one, and hpp->size (size_t) underflows to roughly SIZE_MAX. The next iteration's fmt->entry() then writes into the caller's stack buffer using that bogus size, a second overflow. That same loop also saves bf/size at the top of each iteration but only restored hpp->buf/hpp->size to them before recursing into non-leaf children. Leaf entries left the buffer state advanced from the format loop, so the next sibling in the traversal inherited a shrunk hpp->size and an already-advanced hpp->buf, eventually running hpp->size down to 0 and pointing bf past the end of the stack buffer for the strim(bf) call. Fix the header builder by tracking the write offset and using scnprintf() for each append, same pattern already used elsewhere in this file. Fix the entry loop by clamping the amount passed to advance_hpp() to what's actually left in the buffer, and by restoring hpp->buf/hpp->size unconditionally after formatting each entry instead of only before recursing. Both bugs predate the perf GTK UI's move to GTK 4; neither function is touched by that port. Signed-off-by: Matt Turner --- tools/perf/ui/gtk/hists.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 716dcf02bd0e..80df3fec8ea1 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c @@ -449,7 +449,7 @@ static void perf_gtk__add_hierarchy_entries(struct hist= s *hists, bf =3D hpp->buf; size =3D hpp->size; perf_hpp_list__for_each_format(he->hpp_list, fmt) { - int ret; + int ret, inc; =20 if (fmt->color) ret =3D fmt->color(fmt, hpp, he); @@ -457,15 +457,26 @@ static void perf_gtk__add_hierarchy_entries(struct hi= sts *hists, ret =3D fmt->entry(fmt, hpp, he); =20 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 =3D ret + 2; + if (inc > (int)hpp->size) + inc =3D hpp->size; + advance_hpp(hpp, inc); } =20 gtk_tree_store_set(store, &iter, col_idx, strim(bf), -1); =20 - if (!he->leaf) { - hpp->buf =3D bf; - hpp->size =3D size; + hpp->buf =3D bf; + hpp->size =3D size; =20 + if (!he->leaf) { perf_gtk__add_hierarchy_entries(hists, &he->hroot_out, store, &iter, hpp, min_pcnt); @@ -505,6 +516,7 @@ static void perf_gtk__show_hierarchy(GtkWidget *window,= struct hists *hists, GtkWidget *view; int col_idx; int nr_cols =3D 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] =3D '\0'; first_node =3D true; + ret =3D 0; list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) { if (!first_node) - strcat(buf, " / "); + ret +=3D scnprintf(buf + ret, sizeof(buf) - ret, " / "); first_node =3D false; =20 first_col =3D true; @@ -552,11 +565,11 @@ static void perf_gtk__show_hierarchy(GtkWidget *windo= w, struct hists *hists, continue; =20 if (!first_col) - strcat(buf, "+"); + ret +=3D scnprintf(buf + ret, sizeof(buf) - ret, "+"); first_col =3D false; =20 fmt->header(fmt, &hpp, hists, 0, NULL); - strcat(buf, strim(hpp.buf)); + ret +=3D scnprintf(buf + ret, sizeof(buf) - ret, "%s", strim(hpp.buf)); } } =20 --=20 2.54.0