From nobody Tue Apr 14 22:46:32 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BB4FCC04A68 for ; Fri, 29 Jul 2022 20:08:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239059AbiG2UIF (ORCPT ); Fri, 29 Jul 2022 16:08:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45180 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239046AbiG2UIC (ORCPT ); Fri, 29 Jul 2022 16:08:02 -0400 Received: from mail-pl1-x62a.google.com (mail-pl1-x62a.google.com [IPv6:2607:f8b0:4864:20::62a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1ADE07CB51; Fri, 29 Jul 2022 13:08:01 -0700 (PDT) Received: by mail-pl1-x62a.google.com with SMTP id o3so5529277ple.5; Fri, 29 Jul 2022 13:08:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:from:to:cc; bh=ek+Ls/Nc1cCImKTTFp3CMyhusWpQs8VhRYTO+ff+y+4=; b=VGwDDVVWgD+Qs3NIGLdIQi05fC3drE9GxCopUDL2KFilHBA6Hf5PUFhx/ijHB+//ri i7iY3VjOtiT0gW6uvZI+g1nuW0jb2eHPfSXxAPjZnACQRCAnUwfJci5sblNeC5saM8xN hciKTpTWh63AKwFYfnwL0+1SdMwV1yNWPstS0+IvWvsw8eca4PPBm6V4EtewOxjzgw5g S91dtYJyeu888Hl+oZ+vYNJRtx9hR9A1kAAAe9uZ/OHjKzRefk1IHMbA/6L79OxWCtGs HyKW1MhnJHkg8w93hUIz4EHcXdedz9yCaakOym1IjjMzm3LTN6fO5lzshPG8D0u2LiOT VlnQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:x-gm-message-state:from :to:cc; bh=ek+Ls/Nc1cCImKTTFp3CMyhusWpQs8VhRYTO+ff+y+4=; b=ruwpt54SlUBYTbn/smYursuTLBCikr7qOQbPgKXqsfudZk1WzJ+HMwiC8B3aNw6w73 6cEpGH/Rh+dsMi01HIdHftFc3q3d14x5PirG7SB9WQ1X3dE6cZfOw+AMkfgl38uVM4ah TB3BLcYWdFEEp2CQvr7OmLHRNzG3xVTpgmBmpVHU6VxUNQ1n6yHJcmDmtgYn+cxJkUAt Ed8a4eOL2R7dcM9M0hKIQzpuRTDF5xa/quVGm8dXW6MwxS9eR+z3qNr1V/36KbuKzumU AzeC9WQNnX9nWzEWSNwundTrfNOgI9nfTr6iwUP7zLWJS+KblBKCwbruqx145fZlVv1v WIzQ== X-Gm-Message-State: ACgBeo0kO8d1ZNcW2tUnE3vIujOEys1nN2WInehlZ6ru+5zOcJGLm9bF v7mX3khSdostXQyvVAUFbYxGrmSBhe4= X-Google-Smtp-Source: AA6agR5roehGZYzs6TUhV5HbzfkapFS1wL6KKdunD5/pXZoBE1eR3xon8t6Qvk1Ot8I7eiXXnXPwiQ== X-Received: by 2002:a17:90a:9dc7:b0:1f3:1a8:41a1 with SMTP id x7-20020a17090a9dc700b001f301a841a1mr5977098pjv.23.1659125280379; Fri, 29 Jul 2022 13:08:00 -0700 (PDT) Received: from balhae.hsd1.ca.comcast.net ([2601:647:6780:1010:c01e:2ed5:25ca:3acb]) by smtp.gmail.com with ESMTPSA id o18-20020a170903211200b0016be14a776asm3929823ple.286.2022.07.29.13.07.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 Jul 2022 13:08:00 -0700 (PDT) Sender: Namhyung Kim From: Namhyung Kim To: Arnaldo Carvalho de Melo , Jiri Olsa Cc: Ingo Molnar , Peter Zijlstra , LKML , Ian Rogers , linux-perf-users@vger.kernel.org, Will Deacon , Waiman Long , Boqun Feng , Davidlohr Bueso , Stephane Eranian , Blake Jones Subject: [PATCH 1/3] perf lock: Pass machine pointer to is_lock_function() Date: Fri, 29 Jul 2022 13:07:54 -0700 Message-Id: <20220729200756.666106-2-namhyung@kernel.org> X-Mailer: git-send-email 2.37.1.455.g008518b4e5-goog In-Reply-To: <20220729200756.666106-1-namhyung@kernel.org> References: <20220729200756.666106-1-namhyung@kernel.org> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" This is a preparation for later change to expose the function externally so that it can be used without the implicit session data. Signed-off-by: Namhyung Kim --- tools/perf/builtin-lock.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 041801d8b6ac..10b854315b7a 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -947,10 +947,9 @@ static int report_lock_release_event(struct evsel *evs= el, return 0; } =20 -static bool is_lock_function(u64 addr) +static bool is_lock_function(struct machine *machine, u64 addr) { if (!sched_text_start) { - struct machine *machine =3D &session->machines.host; struct map *kmap; struct symbol *sym; =20 @@ -1002,6 +1001,7 @@ static int lock_contention_caller(struct evsel *evsel= , struct perf_sample *sampl { struct thread *thread; struct callchain_cursor *cursor =3D &callchain_cursor; + struct machine *machine =3D &session->machines.host; struct symbol *sym; int skip =3D 0; int ret; @@ -1010,8 +1010,7 @@ static int lock_contention_caller(struct evsel *evsel= , struct perf_sample *sampl if (show_thread_stats) return -1; =20 - thread =3D machine__findnew_thread(&session->machines.host, - -1, sample->pid); + thread =3D machine__findnew_thread(machine, -1, sample->pid); if (thread =3D=3D NULL) return -1; =20 @@ -1038,7 +1037,7 @@ static int lock_contention_caller(struct evsel *evsel= , struct perf_sample *sampl goto next; =20 sym =3D node->ms.sym; - if (sym && !is_lock_function(node->ip)) { + if (sym && !is_lock_function(machine, node->ip)) { struct map *map =3D node->ms.map; u64 offset; =20 @@ -1060,13 +1059,13 @@ static int lock_contention_caller(struct evsel *evs= el, struct perf_sample *sampl static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample) { struct callchain_cursor *cursor =3D &callchain_cursor; + struct machine *machine =3D &session->machines.host; struct thread *thread; u64 hash =3D 0; int skip =3D 0; int ret; =20 - thread =3D machine__findnew_thread(&session->machines.host, - -1, sample->pid); + thread =3D machine__findnew_thread(machine, -1, sample->pid); if (thread =3D=3D NULL) return -1; =20 @@ -1091,7 +1090,7 @@ static u64 callchain_id(struct evsel *evsel, struct p= erf_sample *sample) if (++skip <=3D CONTENTION_STACK_SKIP) goto next; =20 - if (node->ms.sym && is_lock_function(node->ip)) + if (node->ms.sym && is_lock_function(machine, node->ip)) goto next; =20 hash ^=3D hash_long((unsigned long)node->ip, 64); --=20 2.37.1.455.g008518b4e5-goog From nobody Tue Apr 14 22:46:32 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EA12CC00144 for ; Fri, 29 Jul 2022 20:08:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239081AbiG2UIN (ORCPT ); Fri, 29 Jul 2022 16:08:13 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45270 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239066AbiG2UIH (ORCPT ); Fri, 29 Jul 2022 16:08:07 -0400 Received: from mail-pj1-x1031.google.com (mail-pj1-x1031.google.com [IPv6:2607:f8b0:4864:20::1031]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 91B397968E; Fri, 29 Jul 2022 13:08:02 -0700 (PDT) Received: by mail-pj1-x1031.google.com with SMTP id y1so5531320pja.4; Fri, 29 Jul 2022 13:08:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:from:to:cc; bh=uheR008sA/fPjTwDB5DT1bXOxyIZdFvVc3Lgo5GRou8=; b=HUSPBzVjmbh+59rbVJ99llkksrMqKgjkNkrEC8FaJ//V2iGqbWhnEz+bF59Lsenagx gCtwQtXeN5n5A7c7J5d760a/ZWupHNm9iTzhqQd2FSim0ukXzOlBif+TmL3ZaEiR2D55 2rmSZT/Z+sDy++weCJPCFJHS52Ga26VdRra6enbQZ7sPDg1/+pyOLFyAN+E1rTzVE5pr W6YbQyn0DC9HN2/mnERTryW38KGD4tnGnje6VvSi0/EmhVv4yjz+XzJhszxca2rPhcGK q0Wzdexekba853gh/osa7kvYoFJxjySMl/NFW2x8GWvPNRlHI/PW7qG9+hw/J58M5jJV 7GUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:x-gm-message-state:from :to:cc; bh=uheR008sA/fPjTwDB5DT1bXOxyIZdFvVc3Lgo5GRou8=; b=FCHKDGlGTYJcQHLboEFvAArRQho1EOq95LMTQ6v+reIXK3yMshjN48uHSvydpvwObB h8DZrPy5XPJX3yayTVJfIGXshywYvGTg/BfKqwbHMDbkafoKJ++yFV2hIeFh5SXMeogN F/gtgytIzGMLKcK/nUQVBRVbeFZyKewPshTF1SlzenPkDw81nLrifLO1ydUejQninEFc gyjZ9AodLBCH1FI2rcuIeQJG+s/0i4IAuWzJhQ9cb83vV5EbdX0yiaqva7ZYAY+3p9SD b9lPJaJIuIaFjiZY1S9VdGWWslarp5Mkrqykfq4iS58mWiLrQ53iX6TPuym68uKLjVcm ezpg== X-Gm-Message-State: ACgBeo3XZMP3hqLVmbTPOwc6bWspRpC4vXBeI5/sB8QL4yQ+te9CqxEg lWPwFKVB3k4GI+QpH/ffoVE= X-Google-Smtp-Source: AA6agR6PJBCe2nRGNg6fscS13vaDJ6vXc8QnrX5TBOxr8UutM2XJ7RGE8Ae10D60OondmJb/B+qkUw== X-Received: by 2002:a17:903:41cf:b0:16d:68b4:fcc1 with SMTP id u15-20020a17090341cf00b0016d68b4fcc1mr5406378ple.21.1659125281889; Fri, 29 Jul 2022 13:08:01 -0700 (PDT) Received: from balhae.hsd1.ca.comcast.net ([2601:647:6780:1010:c01e:2ed5:25ca:3acb]) by smtp.gmail.com with ESMTPSA id o18-20020a170903211200b0016be14a776asm3929823ple.286.2022.07.29.13.08.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 Jul 2022 13:08:01 -0700 (PDT) Sender: Namhyung Kim From: Namhyung Kim To: Arnaldo Carvalho de Melo , Jiri Olsa Cc: Ingo Molnar , Peter Zijlstra , LKML , Ian Rogers , linux-perf-users@vger.kernel.org, Will Deacon , Waiman Long , Boqun Feng , Davidlohr Bueso , Stephane Eranian , Blake Jones Subject: [PATCH 2/3] perf lock: Use BPF for lock contention analysis Date: Fri, 29 Jul 2022 13:07:55 -0700 Message-Id: <20220729200756.666106-3-namhyung@kernel.org> X-Mailer: git-send-email 2.37.1.455.g008518b4e5-goog In-Reply-To: <20220729200756.666106-1-namhyung@kernel.org> References: <20220729200756.666106-1-namhyung@kernel.org> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Add -b/--use-bpf option to use BPF to collect lock contention stats. For simplicity it now runs system-wide and requires C-c to stop. Upcoming changes will add the usual filtering. $ sudo perf lock con -b ^C contended total wait max wait avg wait type caller 42 192.67 us 13.64 us 4.59 us spinlock queue_wo= rk_on+0x20 23 85.54 us 10.28 us 3.72 us spinlock worker_t= hread+0x14a 6 13.92 us 6.51 us 2.32 us mutex kernfs_i= op_permission+0x30 3 11.59 us 10.04 us 3.86 us mutex kernfs_d= op_revalidate+0x3c 1 7.52 us 7.52 us 7.52 us spinlock kthread+= 0x115 1 7.24 us 7.24 us 7.24 us rwlock:W sys_epol= l_wait+0x148 2 7.08 us 3.99 us 3.54 us spinlock delayed_= work_timer_fn+0x1b 1 6.41 us 6.41 us 6.41 us spinlock idle_bal= ance+0xa06 2 2.50 us 1.83 us 1.25 us mutex kernfs_i= op_lookup+0x2f 1 1.71 us 1.71 us 1.71 us mutex kernfs_i= op_getattr+0x2c Signed-off-by: Namhyung Kim --- tools/perf/Documentation/perf-lock.txt | 5 + tools/perf/Makefile.perf | 2 +- tools/perf/builtin-lock.c | 164 ++++++------------ tools/perf/util/Build | 1 + tools/perf/util/bpf_lock_contention.c | 132 ++++++++++++++ .../perf/util/bpf_skel/lock_contention.bpf.c | 131 ++++++++++++++ tools/perf/util/lock-contention.h | 133 ++++++++++++++ 7 files changed, 453 insertions(+), 115 deletions(-) create mode 100644 tools/perf/util/bpf_lock_contention.c create mode 100644 tools/perf/util/bpf_skel/lock_contention.bpf.c create mode 100644 tools/perf/util/lock-contention.h diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentat= ion/perf-lock.txt index 8f4e34f924d5..b88bb72c77d4 100644 --- a/tools/perf/Documentation/perf-lock.txt +++ b/tools/perf/Documentation/perf-lock.txt @@ -123,6 +123,11 @@ CONTENTION OPTIONS --threads:: Show per-thread lock contention stat =20 +-b:: +--use-bpf:: + Use BPF program to collect lock contention stats instead of + using the input data. + =20 SEE ALSO -------- diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index d2083a247f73..5053b563bf9c 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -1028,7 +1028,7 @@ SKEL_TMP_OUT :=3D $(abspath $(SKEL_OUT)/.tmp) SKELETONS :=3D $(SKEL_OUT)/bpf_prog_profiler.skel.h SKELETONS +=3D $(SKEL_OUT)/bperf_leader.skel.h $(SKEL_OUT)/bperf_follower.= skel.h SKELETONS +=3D $(SKEL_OUT)/bperf_cgroup.skel.h $(SKEL_OUT)/func_latency.sk= el.h -SKELETONS +=3D $(SKEL_OUT)/off_cpu.skel.h +SKELETONS +=3D $(SKEL_OUT)/off_cpu.skel.h $(SKEL_OUT)/lock_contention.skel= .h SKELETONS +=3D $(SKEL_OUT)/kwork_trace.skel.h =20 $(SKEL_TMP_OUT) $(LIBBPF_OUTPUT): diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 10b854315b7a..29fb6de7fb7d 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -10,6 +10,7 @@ #include "util/thread.h" #include "util/header.h" #include "util/callchain.h" +#include "util/lock-contention.h" =20 #include #include @@ -47,84 +48,11 @@ static struct hlist_head lockhash_table[LOCKHASH_SIZE]; #define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS) #define lockhashentry(key) (lockhash_table + __lockhashfn((key))) =20 -struct lock_stat { - struct hlist_node hash_entry; - struct rb_node rb; /* used for sorting */ - - u64 addr; /* address of lockdep_map, used as ID */ - char *name; /* for strcpy(), we cannot use const */ - - unsigned int nr_acquire; - unsigned int nr_acquired; - unsigned int nr_contended; - unsigned int nr_release; - - union { - unsigned int nr_readlock; - unsigned int flags; - }; - unsigned int nr_trylock; - - /* these times are in nano sec. */ - u64 avg_wait_time; - u64 wait_time_total; - u64 wait_time_min; - u64 wait_time_max; - - int broken; /* flag of blacklist */ - int combined; -}; - -/* - * States of lock_seq_stat - * - * UNINITIALIZED is required for detecting first event of acquire. - * As the nature of lock events, there is no guarantee - * that the first event for the locks are acquire, - * it can be acquired, contended or release. - */ -#define SEQ_STATE_UNINITIALIZED 0 /* initial state */ -#define SEQ_STATE_RELEASED 1 -#define SEQ_STATE_ACQUIRING 2 -#define SEQ_STATE_ACQUIRED 3 -#define SEQ_STATE_READ_ACQUIRED 4 -#define SEQ_STATE_CONTENDED 5 - -/* - * MAX_LOCK_DEPTH - * Imported from include/linux/sched.h. - * Should this be synchronized? - */ -#define MAX_LOCK_DEPTH 48 - -/* - * struct lock_seq_stat: - * Place to put on state of one lock sequence - * 1) acquire -> acquired -> release - * 2) acquire -> contended -> acquired -> release - * 3) acquire (with read or try) -> release - * 4) Are there other patterns? - */ -struct lock_seq_stat { - struct list_head list; - int state; - u64 prev_event_time; - u64 addr; - - int read_count; -}; - -struct thread_stat { - struct rb_node rb; - - u32 tid; - struct list_head seq_list; -}; - static struct rb_root thread_stats; =20 static bool combine_locks; static bool show_thread_stats; +static bool use_bpf; =20 static enum { LOCK_AGGR_ADDR, @@ -132,31 +60,6 @@ static enum { LOCK_AGGR_CALLER, } aggr_mode =3D LOCK_AGGR_ADDR; =20 -/* - * CONTENTION_STACK_DEPTH - * Number of stack trace entries to find callers - */ -#define CONTENTION_STACK_DEPTH 8 - -/* - * CONTENTION_STACK_SKIP - * Number of stack trace entries to skip when finding callers. - * The first few entries belong to the locking implementation itself. - */ -#define CONTENTION_STACK_SKIP 3 - -/* - * flags for lock:contention_begin - * Imported from include/trace/events/lock.h. - */ -#define LCB_F_SPIN (1U << 0) -#define LCB_F_READ (1U << 1) -#define LCB_F_WRITE (1U << 2) -#define LCB_F_RT (1U << 3) -#define LCB_F_PERCPU (1U << 4) -#define LCB_F_MUTEX (1U << 5) - - static u64 sched_text_start; static u64 sched_text_end; static u64 lock_text_start; @@ -947,7 +850,7 @@ static int report_lock_release_event(struct evsel *evse= l, return 0; } =20 -static bool is_lock_function(struct machine *machine, u64 addr) +bool is_lock_function(struct machine *machine, u64 addr) { if (!sched_text_start) { struct map *kmap; @@ -1671,6 +1574,10 @@ static int __cmd_report(bool display_info) return err; } =20 +static void sighandler(int sig __maybe_unused) +{ +} + static int __cmd_contention(void) { int err =3D -EINVAL; @@ -1686,7 +1593,7 @@ static int __cmd_contention(void) .force =3D force, }; =20 - session =3D perf_session__new(&data, &eops); + session =3D perf_session__new(use_bpf ? NULL : &data, &eops); if (IS_ERR(session)) { pr_err("Initializing perf session failed\n"); return PTR_ERR(session); @@ -1696,17 +1603,30 @@ static int __cmd_contention(void) symbol_conf.sort_by_name =3D true; symbol__init(&session->header.env); =20 - if (!perf_session__has_traces(session, "lock record")) - goto out_delete; + if (use_bpf) { + if (lock_contention_prepare() < 0) { + pr_err("lock contention BPF setup failed\n"); + return -1; + } =20 - if (!evlist__find_evsel_by_str(session->evlist, "lock:contention_begin"))= { - pr_err("lock contention evsel not found\n"); - goto out_delete; - } + signal(SIGINT, sighandler); + signal(SIGCHLD, sighandler); + signal(SIGTERM, sighandler); + } else { + if (!perf_session__has_traces(session, "lock record")) + goto out_delete; =20 - if (perf_session__set_tracepoints_handlers(session, contention_tracepoint= s)) { - pr_err("Initializing perf session tracepoint handlers failed\n"); - goto out_delete; + if (!evlist__find_evsel_by_str(session->evlist, + "lock:contention_begin")) { + pr_err("lock contention evsel not found\n"); + goto out_delete; + } + + if (perf_session__set_tracepoints_handlers(session, + contention_tracepoints)) { + pr_err("Initializing perf session tracepoint handlers failed\n"); + goto out_delete; + } } =20 if (setup_output_field(true, output_fields)) @@ -1720,9 +1640,19 @@ static int __cmd_contention(void) else aggr_mode =3D LOCK_AGGR_CALLER; =20 - err =3D perf_session__process_events(session); - if (err) - goto out_delete; + if (use_bpf) { + lock_contention_start(); + + /* wait for signal */ + pause(); + + lock_contention_stop(); + lock_contention_read(&session->machines.host, &lockhash_table[0]); + } else { + err =3D perf_session__process_events(session); + if (err) + goto out_delete; + } =20 setup_pager(); =20 @@ -1730,6 +1660,7 @@ static int __cmd_contention(void) print_contention_result(); =20 out_delete: + lock_contention_finish(); perf_session__delete(session); return err; } @@ -1853,13 +1784,14 @@ int cmd_lock(int argc, const char **argv) OPT_PARENT(lock_options) }; =20 - const struct option contention_options[] =3D { + struct option contention_options[] =3D { OPT_STRING('k', "key", &sort_key, "wait_total", "key for sorting (contended / wait_total / wait_max / wait_min / avg= _wait)"), OPT_STRING('F', "field", &output_fields, "contended,wait_total,wait_max,a= vg_wait", "output fields (contended / wait_total / wait_max / wait_min / avg_w= ait)"), OPT_BOOLEAN('t', "threads", &show_thread_stats, "show per-thread lock stats"), + OPT_BOOLEAN('b', "use-bpf", &use_bpf, "use BPF program to collect lock co= ntention stats"), OPT_PARENT(lock_options) }; =20 @@ -1922,6 +1854,10 @@ int cmd_lock(int argc, const char **argv) sort_key =3D "wait_total"; output_fields =3D "contended,wait_total,wait_max,avg_wait"; =20 +#ifndef HAVE_BPF_SKEL + set_option_nobuild(contention_options, 'b', "use-bpf", + "no BUILD_BPF_SKEL=3D1", false); +#endif if (argc) { argc =3D parse_options(argc, argv, contention_options, contention_usage, 0); diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 66ad30cf65ec..86fcd0a614fe 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -149,6 +149,7 @@ perf-$(CONFIG_PERF_BPF_SKEL) +=3D bpf_counter_cgroup.o perf-$(CONFIG_PERF_BPF_SKEL) +=3D bpf_ftrace.o perf-$(CONFIG_PERF_BPF_SKEL) +=3D bpf_off_cpu.o perf-$(CONFIG_PERF_BPF_SKEL) +=3D bpf_kwork.o +perf-$(CONFIG_PERF_BPF_SKEL) +=3D bpf_lock_contention.o perf-$(CONFIG_BPF_PROLOGUE) +=3D bpf-prologue.o perf-$(CONFIG_LIBELF) +=3D symbol-elf.o perf-$(CONFIG_LIBELF) +=3D probe-file.o diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lo= ck_contention.c new file mode 100644 index 000000000000..8eb33e6f5029 --- /dev/null +++ b/tools/perf/util/bpf_lock_contention.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "util/debug.h" +#include "util/machine.h" +#include "util/map.h" +#include "util/symbol.h" +#include "util/lock-contention.h" +#include +#include + +#include "bpf_skel/lock_contention.skel.h" + +static struct lock_contention_bpf *skel; + +/* should be same as bpf_skel/lock_contention.bpf.c */ +struct lock_contention_key { + u32 stack_id; +}; + +struct lock_contention_data { + u64 total_time; + u64 min_time; + u64 max_time; + u32 count; + u32 flags; +}; + +int lock_contention_prepare(void) +{ + skel =3D lock_contention_bpf__open(); + if (!skel) { + pr_err("Failed to open lock-contention BPF skeleton\n"); + return -1; + } + + if (lock_contention_bpf__load(skel) < 0) { + pr_err("Failed to load lock-contention BPF skeleton\n"); + return -1; + } + + lock_contention_bpf__attach(skel); + return 0; +} + +int lock_contention_start(void) +{ + skel->bss->enabled =3D 1; + return 0; +} + +int lock_contention_stop(void) +{ + skel->bss->enabled =3D 0; + return 0; +} + +int lock_contention_read(struct machine *machine, struct hlist_head *head) +{ + int fd, stack; + u32 prev_key, key; + struct lock_contention_data data; + struct lock_stat *st; + u64 stack_trace[CONTENTION_STACK_DEPTH]; + + fd =3D bpf_map__fd(skel->maps.lock_stat); + stack =3D bpf_map__fd(skel->maps.stacks); + + prev_key =3D 0; + while (!bpf_map_get_next_key(fd, &prev_key, &key)) { + struct map *kmap; + struct symbol *sym; + int idx; + + bpf_map_lookup_elem(fd, &key, &data); + st =3D zalloc(sizeof(*st)); + if (st =3D=3D NULL) + return -1; + + st->nr_contended =3D data.count; + st->wait_time_total =3D data.total_time; + st->wait_time_max =3D data.max_time; + st->wait_time_min =3D data.min_time; + + if (data.count) + st->avg_wait_time =3D data.total_time / data.count; + + st->flags =3D data.flags; + + bpf_map_lookup_elem(stack, &key, stack_trace); + + /* skip BPF + lock internal functions */ + idx =3D CONTENTION_STACK_SKIP; + while (is_lock_function(machine, stack_trace[idx]) && + idx < CONTENTION_STACK_DEPTH - 1) + idx++; + + st->addr =3D stack_trace[idx]; + sym =3D machine__find_kernel_symbol(machine, st->addr, &kmap); + + if (sym) { + unsigned long offset; + int ret =3D 0; + + offset =3D kmap->map_ip(kmap, st->addr) - sym->start; + + if (offset) + ret =3D asprintf(&st->name, "%s+%#lx", sym->name, offset); + else + st->name =3D strdup(sym->name); + + if (ret < 0 || st->name =3D=3D NULL) + return -1; + } else if (asprintf(&st->name, "%#lx", (unsigned long)st->addr) < 0) { + free(st); + return -1; + } + + hlist_add_head(&st->hash_entry, head); + prev_key =3D key; + } + + return 0; +} + +int lock_contention_finish(void) +{ + if (skel) { + skel->bss->enabled =3D 0; + lock_contention_bpf__destroy(skel); + } + + return 0; +} diff --git a/tools/perf/util/bpf_skel/lock_contention.bpf.c b/tools/perf/ut= il/bpf_skel/lock_contention.bpf.c new file mode 100644 index 000000000000..5d1c7641223f --- /dev/null +++ b/tools/perf/util/bpf_skel/lock_contention.bpf.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +// Copyright (c) 2022 Google +#include "vmlinux.h" +#include +#include +#include + +/* maximum stack trace depth */ +#define MAX_STACKS 8 + +/* default buffer size */ +#define MAX_ENTRIES 10240 + +struct contention_key { + __u32 stack_id; +}; + +struct contention_data { + __u64 total_time; + __u64 min_time; + __u64 max_time; + __u32 count; + __u32 flags; +}; + +struct tstamp_data { + __u64 timestamp; + __u64 lock; + __u32 flags; + __u32 stack_id; +}; + +/* callstack storage */ +struct { + __uint(type, BPF_MAP_TYPE_STACK_TRACE); + __uint(key_size, sizeof(__u32)); + __uint(value_size, MAX_STACKS * sizeof(__u64)); + __uint(max_entries, MAX_ENTRIES); +} stacks SEC(".maps"); + +/* maintain timestamp at the beginning of contention */ +struct { + __uint(type, BPF_MAP_TYPE_TASK_STORAGE); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, int); + __type(value, struct tstamp_data); +} tstamp SEC(".maps"); + +/* actual lock contention statistics */ +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(key_size, sizeof(struct contention_key)); + __uint(value_size, sizeof(struct contention_data)); + __uint(max_entries, MAX_ENTRIES); +} lock_stat SEC(".maps"); + +/* control flags */ +int enabled; + +SEC("tp_btf/contention_begin") +int contention_begin(u64 *ctx) +{ + struct task_struct *curr; + struct tstamp_data *pelem; + + if (!enabled) + return 0; + + curr =3D bpf_get_current_task_btf(); + pelem =3D bpf_task_storage_get(&tstamp, curr, NULL, + BPF_LOCAL_STORAGE_GET_F_CREATE); + if (!pelem || pelem->lock) + return 0; + + pelem->timestamp =3D bpf_ktime_get_ns(); + pelem->lock =3D (__u64)ctx[0]; + pelem->flags =3D (__u32)ctx[1]; + pelem->stack_id =3D bpf_get_stackid(ctx, &stacks, BPF_F_FAST_STACK_CMP); + + return 0; +} + +SEC("tp_btf/contention_end") +int contention_end(u64 *ctx) +{ + struct task_struct *curr; + struct tstamp_data *pelem; + struct contention_key key; + struct contention_data *data; + __u64 duration; + + if (!enabled) + return 0; + + curr =3D bpf_get_current_task_btf(); + pelem =3D bpf_task_storage_get(&tstamp, curr, NULL, 0); + if (!pelem || pelem->lock !=3D ctx[0]) + return 0; + + duration =3D bpf_ktime_get_ns() - pelem->timestamp; + + key.stack_id =3D pelem->stack_id; + data =3D bpf_map_lookup_elem(&lock_stat, &key); + if (!data) { + struct contention_data first =3D { + .total_time =3D duration, + .max_time =3D duration, + .min_time =3D duration, + .count =3D 1, + .flags =3D pelem->flags, + }; + + bpf_map_update_elem(&lock_stat, &key, &first, BPF_NOEXIST); + pelem->lock =3D 0; + return 0; + } + + __sync_fetch_and_add(&data->total_time, duration); + __sync_fetch_and_add(&data->count, 1); + + /* FIXME: need atomic operations */ + if (data->max_time < duration) + data->max_time =3D duration; + if (data->min_time > duration) + data->min_time =3D duration; + + pelem->lock =3D 0; + return 0; +} + +char LICENSE[] SEC("license") =3D "Dual BSD/GPL"; diff --git a/tools/perf/util/lock-contention.h b/tools/perf/util/lock-conte= ntion.h new file mode 100644 index 000000000000..c92db4a47d8d --- /dev/null +++ b/tools/perf/util/lock-contention.h @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef PERF_LOCK_CONTENTION_H +#define PERF_LOCK_CONTENTION_H + +#include +#include + +struct lock_stat { + struct hlist_node hash_entry; + struct rb_node rb; /* used for sorting */ + + u64 addr; /* address of lockdep_map, used as ID */ + char *name; /* for strcpy(), we cannot use const */ + + unsigned int nr_acquire; + unsigned int nr_acquired; + unsigned int nr_contended; + unsigned int nr_release; + + union { + unsigned int nr_readlock; + unsigned int flags; + }; + unsigned int nr_trylock; + + /* these times are in nano sec. */ + u64 avg_wait_time; + u64 wait_time_total; + u64 wait_time_min; + u64 wait_time_max; + + int broken; /* flag of blacklist */ + int combined; +}; + +/* + * States of lock_seq_stat + * + * UNINITIALIZED is required for detecting first event of acquire. + * As the nature of lock events, there is no guarantee + * that the first event for the locks are acquire, + * it can be acquired, contended or release. + */ +#define SEQ_STATE_UNINITIALIZED 0 /* initial state */ +#define SEQ_STATE_RELEASED 1 +#define SEQ_STATE_ACQUIRING 2 +#define SEQ_STATE_ACQUIRED 3 +#define SEQ_STATE_READ_ACQUIRED 4 +#define SEQ_STATE_CONTENDED 5 + +/* + * MAX_LOCK_DEPTH + * Imported from include/linux/sched.h. + * Should this be synchronized? + */ +#define MAX_LOCK_DEPTH 48 + +/* + * struct lock_seq_stat: + * Place to put on state of one lock sequence + * 1) acquire -> acquired -> release + * 2) acquire -> contended -> acquired -> release + * 3) acquire (with read or try) -> release + * 4) Are there other patterns? + */ +struct lock_seq_stat { + struct list_head list; + int state; + u64 prev_event_time; + u64 addr; + + int read_count; +}; + +struct thread_stat { + struct rb_node rb; + + u32 tid; + struct list_head seq_list; +}; + +/* + * CONTENTION_STACK_DEPTH + * Number of stack trace entries to find callers + */ +#define CONTENTION_STACK_DEPTH 8 + +/* + * CONTENTION_STACK_SKIP + * Number of stack trace entries to skip when finding callers. + * The first few entries belong to the locking implementation itself. + */ +#define CONTENTION_STACK_SKIP 3 + +/* + * flags for lock:contention_begin + * Imported from include/trace/events/lock.h. + */ +#define LCB_F_SPIN (1U << 0) +#define LCB_F_READ (1U << 1) +#define LCB_F_WRITE (1U << 2) +#define LCB_F_RT (1U << 3) +#define LCB_F_PERCPU (1U << 4) +#define LCB_F_MUTEX (1U << 5) + +struct machine; + +#ifdef HAVE_BPF_SKEL + +int lock_contention_prepare(void); +int lock_contention_start(void); +int lock_contention_stop(void); +int lock_contention_read(struct machine *machine, struct hlist_head *head); +int lock_contention_finish(void); + +#else /* !HAVE_BPF_SKEL */ + +static inline int lock_contention_prepare(void) { return 0; } +static inline int lock_contention_start(void) { return 0; } +static inline int lock_contention_stop(void) { return 0; } +static inline int lock_contention_finish(void) { return 0; } + +static inline int lock_contention_read(struct machine *machine __maybe_unu= sed, + struct hlist_head *head __maybe_unused) +{ + return 0; +} + +#endif /* HAVE_BPF_SKEL */ + +bool is_lock_function(struct machine *machine, u64 addr); + +#endif /* PERF_LOCK_CONTENTION_H */ --=20 2.37.1.455.g008518b4e5-goog From nobody Tue Apr 14 22:46:32 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7F368C04A68 for ; Fri, 29 Jul 2022 20:08:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S239088AbiG2UIP (ORCPT ); Fri, 29 Jul 2022 16:08:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:45282 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S239072AbiG2UII (ORCPT ); Fri, 29 Jul 2022 16:08:08 -0400 Received: from mail-pf1-x42e.google.com (mail-pf1-x42e.google.com [IPv6:2607:f8b0:4864:20::42e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1E3677B78C; Fri, 29 Jul 2022 13:08:04 -0700 (PDT) Received: by mail-pf1-x42e.google.com with SMTP id e16so5524589pfm.11; Fri, 29 Jul 2022 13:08:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:from:to:cc; bh=ye+dSJrlwAlKlFHrRKUpFTZfgb82jZyEHkZgl3F3p7A=; b=p1vVPdaUl8EMKHaYvTidHorG68HqilA2WskBsqCB/M38mX19YqS55xnyowie+eD8o5 IvHO6C8hcQ62rhaFM8Ion+Ius+D89QMFTMSsLtxr8Yso6EWDub0KwB7rsoo9Ce8KTLxe HHq5PNARUu6ITHIFeDfL9O8gGN0t919wWqLnY6LoQFZG+S0oVNAJUx/dES+8afo2Hdil 1YlLOefj/rh5CcdVfJJAD9C/X1VTPGYYS285ndxon/J5SOArUHjzg+YSF39TH7hktyaI a9NI6Gb2iKtEgs/aDvkyR2enTR0uARluf1amawBI8gbEmfmugSy1Ed2BO8f7oFwhG7yD AwoA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:x-gm-message-state:from :to:cc; bh=ye+dSJrlwAlKlFHrRKUpFTZfgb82jZyEHkZgl3F3p7A=; b=32Xw/ui/IGYSBOf5SYMxb4F9/v8ZtND3Tj6VPbNJOiInYtYefGXvltsIF9W6M9ncer ZpHTS7TNVTXhRL/6z9X2tUxIaL47A8GtwOaZnx+FPX2I/eeRpZCVNVdUGS9AgW7iNHcX kMU+hk7VWHHE5KB1+IFxoLjvFH+/PjKafHNeIQjgNNy09lKl4OifkR0Q7hBQiStx1Zkj 5oJEMPe+pezy07qpR3yznCzavn/aBj6i+Nf59e3Miwn97XkAkd/QrCITMduICPF1H1V8 S6GSqBa6uP62Y4BdIHW+aE2qqyQ057w6xG6iQQyPzE/1tLc1XNeMKXjNOyXx0wSttLlB m+rg== X-Gm-Message-State: AJIora/IiHoPfIWRjTo/8GtIHPXQbdgHSig/bQHHgOsTQBsms4it2d+s EUe3s99HEcfd9jCxFu/lKvc= X-Google-Smtp-Source: AGRyM1vhVWpYALEjCVjxj2CextGKUtmpYslqT3p+aZjPEzdziU7MAK6Z8OOWCEnmW3H+/jfzBoqZTA== X-Received: by 2002:a63:8bc7:0:b0:41b:84d3:408 with SMTP id j190-20020a638bc7000000b0041b84d30408mr4314157pge.255.1659125283436; Fri, 29 Jul 2022 13:08:03 -0700 (PDT) Received: from balhae.hsd1.ca.comcast.net ([2601:647:6780:1010:c01e:2ed5:25ca:3acb]) by smtp.gmail.com with ESMTPSA id o18-20020a170903211200b0016be14a776asm3929823ple.286.2022.07.29.13.08.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 29 Jul 2022 13:08:03 -0700 (PDT) Sender: Namhyung Kim From: Namhyung Kim To: Arnaldo Carvalho de Melo , Jiri Olsa Cc: Ingo Molnar , Peter Zijlstra , LKML , Ian Rogers , linux-perf-users@vger.kernel.org, Will Deacon , Waiman Long , Boqun Feng , Davidlohr Bueso , Stephane Eranian , Blake Jones Subject: [PATCH 3/3] perf lock: Implement cpu and task filters for BPF Date: Fri, 29 Jul 2022 13:07:56 -0700 Message-Id: <20220729200756.666106-4-namhyung@kernel.org> X-Mailer: git-send-email 2.37.1.455.g008518b4e5-goog In-Reply-To: <20220729200756.666106-1-namhyung@kernel.org> References: <20220729200756.666106-1-namhyung@kernel.org> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Add -a/--all-cpus and -C/--cpu options for cpu filtering. Also -p/--pid and --tid options are added for task filtering. The short -t option is taken for --threads already. Tracking the command line workload is possible as well. $ sudo perf lock contention -a -b sleep 1 Signed-off-by: Namhyung Kim --- tools/perf/Documentation/perf-lock.txt | 17 ++++++ tools/perf/builtin-lock.c | 55 ++++++++++++++++--- tools/perf/util/bpf_lock_contention.c | 51 ++++++++++++++++- .../perf/util/bpf_skel/lock_contention.bpf.c | 41 +++++++++++++- tools/perf/util/lock-contention.h | 11 +++- 5 files changed, 162 insertions(+), 13 deletions(-) diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentat= ion/perf-lock.txt index b88bb72c77d4..7949d2e6891b 100644 --- a/tools/perf/Documentation/perf-lock.txt +++ b/tools/perf/Documentation/perf-lock.txt @@ -128,6 +128,23 @@ CONTENTION OPTIONS Use BPF program to collect lock contention stats instead of using the input data. =20 +-a:: +--all-cpus:: + System-wide collection from all CPUs. + +-C:: +--cpu:: + Collect samples only on the list of CPUs provided. Multiple CPUs can be + provided as a comma-separated list with no space: 0,1. Ranges of CPUs + are specified with -: 0-2. Default is to monitor all CPUs. + +-p:: +--pid=3D:: + Record events on existing process ID (comma separated list). + +--tid=3D:: + Record events on existing thread ID (comma separated list). + =20 SEE ALSO -------- diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 29fb6de7fb7d..7897a33fec1b 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -9,6 +9,7 @@ #include "util/symbol.h" #include "util/thread.h" #include "util/header.h" +#include "util/target.h" #include "util/callchain.h" #include "util/lock-contention.h" =20 @@ -38,6 +39,7 @@ #include =20 static struct perf_session *session; +static struct target target; =20 /* based on kernel/lockdep.c */ #define LOCKHASH_BITS 12 @@ -1578,7 +1580,7 @@ static void sighandler(int sig __maybe_unused) { } =20 -static int __cmd_contention(void) +static int __cmd_contention(int argc, const char **argv) { int err =3D -EINVAL; struct perf_tool eops =3D { @@ -1592,6 +1594,7 @@ static int __cmd_contention(void) .mode =3D PERF_DATA_MODE_READ, .force =3D force, }; + struct evlist *evlist =3D NULL; =20 session =3D perf_session__new(use_bpf ? NULL : &data, &eops); if (IS_ERR(session)) { @@ -1604,14 +1607,40 @@ static int __cmd_contention(void) symbol__init(&session->header.env); =20 if (use_bpf) { - if (lock_contention_prepare() < 0) { - pr_err("lock contention BPF setup failed\n"); - return -1; + err =3D target__validate(&target); + if (err) { + char errbuf[512]; + + target__strerror(&target, err, errbuf, 512); + pr_err("%s\n", errbuf); + goto out_delete; } =20 signal(SIGINT, sighandler); signal(SIGCHLD, sighandler); signal(SIGTERM, sighandler); + + evlist =3D evlist__new(); + if (evlist =3D=3D NULL) { + err =3D -ENOMEM; + goto out_delete; + } + + err =3D evlist__create_maps(evlist, &target); + if (err < 0) + goto out_delete; + + if (argc) { + err =3D evlist__prepare_workload(evlist, &target, + argv, false, NULL); + if (err < 0) + goto out_delete; + } + + if (lock_contention_prepare(evlist, &target) < 0) { + pr_err("lock contention BPF setup failed\n"); + goto out_delete; + } } else { if (!perf_session__has_traces(session, "lock record")) goto out_delete; @@ -1642,6 +1671,8 @@ static int __cmd_contention(void) =20 if (use_bpf) { lock_contention_start(); + if (argc) + evlist__start_workload(evlist); =20 /* wait for signal */ pause(); @@ -1660,6 +1691,7 @@ static int __cmd_contention(void) print_contention_result(); =20 out_delete: + evlist__delete(evlist); lock_contention_finish(); perf_session__delete(session); return err; @@ -1792,6 +1824,15 @@ int cmd_lock(int argc, const char **argv) OPT_BOOLEAN('t', "threads", &show_thread_stats, "show per-thread lock stats"), OPT_BOOLEAN('b', "use-bpf", &use_bpf, "use BPF program to collect lock co= ntention stats"), + OPT_BOOLEAN('a', "all-cpus", &target.system_wide, + "System-wide collection from all CPUs"), + OPT_STRING('C', "cpu", &target.cpu_list, "cpu", + "List of cpus to monitor"), + OPT_STRING('p', "pid", &target.pid, "pid", + "Trace on existing process id"), + /* TODO: Add short option -t after -t/--tracer can be removed. */ + OPT_STRING(0, "tid", &target.tid, "tid", + "Trace on existing thread id (exclusive to --pid)"), OPT_PARENT(lock_options) }; =20 @@ -1861,12 +1902,8 @@ int cmd_lock(int argc, const char **argv) if (argc) { argc =3D parse_options(argc, argv, contention_options, contention_usage, 0); - if (argc) { - usage_with_options(contention_usage, - contention_options); - } } - rc =3D __cmd_contention(); + rc =3D __cmd_contention(argc, argv); } else { usage_with_options(lock_usage, lock_options); } diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lo= ck_contention.c index 8eb33e6f5029..16b7451b4b09 100644 --- a/tools/perf/util/bpf_lock_contention.c +++ b/tools/perf/util/bpf_lock_contention.c @@ -1,8 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 #include "util/debug.h" +#include "util/evlist.h" #include "util/machine.h" #include "util/map.h" #include "util/symbol.h" +#include "util/target.h" +#include "util/thread_map.h" #include "util/lock-contention.h" #include #include @@ -24,19 +27,65 @@ struct lock_contention_data { u32 flags; }; =20 -int lock_contention_prepare(void) +int lock_contention_prepare(struct evlist *evlist, struct target *target) { + int i, fd; + int ncpus =3D 1, ntasks =3D 1; + skel =3D lock_contention_bpf__open(); if (!skel) { pr_err("Failed to open lock-contention BPF skeleton\n"); return -1; } =20 + if (target__has_cpu(target)) + ncpus =3D perf_cpu_map__nr(evlist->core.user_requested_cpus); + if (target__has_task(target)) + ntasks =3D perf_thread_map__nr(evlist->core.threads); + + bpf_map__set_max_entries(skel->maps.cpu_filter, ncpus); + bpf_map__set_max_entries(skel->maps.task_filter, ntasks); + if (lock_contention_bpf__load(skel) < 0) { pr_err("Failed to load lock-contention BPF skeleton\n"); return -1; } =20 + if (target__has_cpu(target)) { + u32 cpu; + u8 val =3D 1; + + skel->bss->has_cpu =3D 1; + fd =3D bpf_map__fd(skel->maps.cpu_filter); + + for (i =3D 0; i < ncpus; i++) { + cpu =3D perf_cpu_map__cpu(evlist->core.user_requested_cpus, i).cpu; + bpf_map_update_elem(fd, &cpu, &val, BPF_ANY); + } + } + + if (target__has_task(target)) { + u32 pid; + u8 val =3D 1; + + skel->bss->has_task =3D 1; + fd =3D bpf_map__fd(skel->maps.task_filter); + + for (i =3D 0; i < ntasks; i++) { + pid =3D perf_thread_map__pid(evlist->core.threads, i); + bpf_map_update_elem(fd, &pid, &val, BPF_ANY); + } + } + + if (target__none(target) && evlist->workload.pid > 0) { + u32 pid =3D evlist->workload.pid; + u8 val =3D 1; + + skel->bss->has_task =3D 1; + fd =3D bpf_map__fd(skel->maps.task_filter); + bpf_map_update_elem(fd, &pid, &val, BPF_ANY); + } + lock_contention_bpf__attach(skel); return 0; } diff --git a/tools/perf/util/bpf_skel/lock_contention.bpf.c b/tools/perf/ut= il/bpf_skel/lock_contention.bpf.c index 5d1c7641223f..67d46533e518 100644 --- a/tools/perf/util/bpf_skel/lock_contention.bpf.c +++ b/tools/perf/util/bpf_skel/lock_contention.bpf.c @@ -54,8 +54,47 @@ struct { __uint(max_entries, MAX_ENTRIES); } lock_stat SEC(".maps"); =20 +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(__u8)); + __uint(max_entries, 1); +} cpu_filter SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_HASH); + __uint(key_size, sizeof(__u32)); + __uint(value_size, sizeof(__u8)); + __uint(max_entries, 1); +} task_filter SEC(".maps"); + /* control flags */ int enabled; +int has_cpu; +int has_task; + +static inline int can_record(void) +{ + if (has_cpu) { + __u32 cpu =3D bpf_get_smp_processor_id(); + __u8 *ok; + + ok =3D bpf_map_lookup_elem(&cpu_filter, &cpu); + if (!ok) + return 0; + } + + if (has_task) { + __u8 *ok; + __u32 pid =3D bpf_get_current_pid_tgid(); + + ok =3D bpf_map_lookup_elem(&task_filter, &pid); + if (!ok) + return 0; + } + + return 1; +} =20 SEC("tp_btf/contention_begin") int contention_begin(u64 *ctx) @@ -63,7 +102,7 @@ int contention_begin(u64 *ctx) struct task_struct *curr; struct tstamp_data *pelem; =20 - if (!enabled) + if (!enabled || !can_record()) return 0; =20 curr =3D bpf_get_current_task_btf(); diff --git a/tools/perf/util/lock-contention.h b/tools/perf/util/lock-conte= ntion.h index c92db4a47d8d..092c84441f9f 100644 --- a/tools/perf/util/lock-contention.h +++ b/tools/perf/util/lock-contention.h @@ -103,11 +103,13 @@ struct thread_stat { #define LCB_F_PERCPU (1U << 4) #define LCB_F_MUTEX (1U << 5) =20 +struct evlist; struct machine; +struct target; =20 #ifdef HAVE_BPF_SKEL =20 -int lock_contention_prepare(void); +int lock_contention_prepare(struct evlist *evlist, struct target *target); int lock_contention_start(void); int lock_contention_stop(void); int lock_contention_read(struct machine *machine, struct hlist_head *head); @@ -115,7 +117,12 @@ int lock_contention_finish(void); =20 #else /* !HAVE_BPF_SKEL */ =20 -static inline int lock_contention_prepare(void) { return 0; } +static inline int lock_contention_prepare(struct evlist *evlist __maybe_un= used, + struct target *target __maybe_unused) +{ + return 0; +} + static inline int lock_contention_start(void) { return 0; } static inline int lock_contention_stop(void) { return 0; } static inline int lock_contention_finish(void) { return 0; } --=20 2.37.1.455.g008518b4e5-goog