From: Ian Rogers <irogers@google.com>
To: irogers@google.com, acme@kernel.org, namhyung@kernel.org
Cc: adrian.hunter@intel.com, james.clark@linaro.org,
jolsa@kernel.org, linux-kernel@vger.kernel.org,
linux-perf-users@vger.kernel.org, mingo@redhat.com,
peterz@infradead.org, rcu@vger.kernel.org
Subject: [PATCH v2 3/6] perf test: Add tests for lock_held() BPF sample filter
Date: Sat, 17 Oct 2026 10:14:34 -0700 [thread overview]
Message-ID: <20261017171437.2066343-4-irogers@google.com> (raw)
In-Reply-To: <20261017171437.2066343-1-irogers@google.com>
Add unit tests in parse-events.c for parsing lock_held(),
lock_held_read(), and lock_held_exclusive() BPF filter expressions
(standalone predicates, negated predicates, and compound group
expressions) and extend record_bpf_filter.sh with lock_held(),
lock_held_read()/lock_held_write(), and ':R'/':W' recording tests.
Example output:
# perf test -v 'Parsing of BPF sample filters'
5: Parse event definition strings :
5.7: Parsing of BPF sample filters : Ok
# perf test -v 'perf record sample filtering (by BPF) tests'
112: perf record sample filtering (by BPF) tests :
Checking BPF-filter privilege
Basic bpf-filter test
Basic bpf-filter test [Success]
Failing bpf-filter test
Failing bpf-filter test [Success]
Group bpf-filter test
Group bpf-filter test [Success]
Multiple bpf-filter test
Multiple bpf-filter test [Success]
Cgroup bpf-filter test
Cgroup bpf-filter test [Success]
Lock-held bpf-filter test
Lock-held bpf-filter test [Success]
112: perf record sample filtering (by BPF) tests : Ok
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/parse-events.c | 108 ++++++++++++++++++++
tools/perf/tests/shell/record_bpf_filter.sh | 103 +++++++++++++++++++
2 files changed, 211 insertions(+)
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c
index 5f6f95c8a5b4..4cf9192762f2 100644
--- a/tools/perf/tests/parse-events.c
+++ b/tools/perf/tests/parse-events.c
@@ -8,9 +8,11 @@
#include "debug.h"
#include "pmu.h"
#include "pmus.h"
+#include "bpf-filter.h"
#include "strbuf.h"
#include <dirent.h>
#include <errno.h>
+#include <string.h>
#include "fncache.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -2938,6 +2940,111 @@ static int test__pmu_events_alias2(struct test_suite *test __maybe_unused,
return ret;
}
+#ifdef HAVE_BPF_SKEL
+static void clear_bpf_filter_exprs(struct list_head *expr_head)
+{
+ struct perf_bpf_filter_expr *expr, *tmp;
+
+ list_for_each_entry_safe(expr, tmp, expr_head, list) {
+ list_del(&expr->list);
+ perf_bpf_filter_expr__delete(expr);
+ }
+}
+
+static int do_test_bpf_filters(struct list_head *expr_head)
+{
+ struct perf_bpf_filter_expr *expr, *group;
+ const char *compound = "lock_held(rwsem:R) == 1 || lock_held(&dentry) != 0, period > 100";
+
+ /* Simple lock_held(&cgroup_mutex) */
+ TEST_ASSERT_VAL("parse lock_held(&cgroup_mutex)",
+ __perf_bpf_filter__parse(expr_head, "lock_held(&cgroup_mutex)") == 0);
+ TEST_ASSERT_VAL("not empty", !list_empty(expr_head));
+ expr = list_first_entry(expr_head, struct perf_bpf_filter_expr, list);
+ TEST_ASSERT_VAL("term is PBF_TERM_LOCK_HELD", expr->term == PBF_TERM_LOCK_HELD);
+ TEST_ASSERT_VAL("op is PBF_OP_EQ", expr->op == PBF_OP_EQ);
+ TEST_ASSERT_VAL("val is 1", expr->val == 1);
+ TEST_ASSERT_VAL("lock_name is &cgroup_mutex",
+ expr->lock_name && !strcmp(expr->lock_name, "&cgroup_mutex"));
+ clear_bpf_filter_exprs(expr_head);
+
+ /* Negated !lock_held(cgroup_mutex) */
+ TEST_ASSERT_VAL("parse !lock_held(cgroup_mutex)",
+ __perf_bpf_filter__parse(expr_head, "!lock_held(cgroup_mutex)") == 0);
+ TEST_ASSERT_VAL("not empty", !list_empty(expr_head));
+ expr = list_first_entry(expr_head, struct perf_bpf_filter_expr, list);
+ TEST_ASSERT_VAL("term is PBF_TERM_LOCK_HELD", expr->term == PBF_TERM_LOCK_HELD);
+ TEST_ASSERT_VAL("op is PBF_OP_EQ", expr->op == PBF_OP_EQ);
+ TEST_ASSERT_VAL("val is 0", expr->val == 0);
+ TEST_ASSERT_VAL("lock_name is cgroup_mutex",
+ expr->lock_name && !strcmp(expr->lock_name, "cgroup_mutex"));
+ clear_bpf_filter_exprs(expr_head);
+
+ /* Read/shared and write/exclusive predicates */
+ TEST_ASSERT_VAL("parse lock_held_read(&mmap_lock)",
+ __perf_bpf_filter__parse(expr_head, "lock_held_read(&mmap_lock)") == 0);
+ TEST_ASSERT_VAL("not empty", !list_empty(expr_head));
+ expr = list_first_entry(expr_head, struct perf_bpf_filter_expr, list);
+ TEST_ASSERT_VAL("term is PBF_TERM_LOCK_HELD", expr->term == PBF_TERM_LOCK_HELD);
+ TEST_ASSERT_VAL("part is PBF_LOCK_MODE_READ", expr->part == PBF_LOCK_MODE_READ);
+ TEST_ASSERT_VAL("op is PBF_OP_EQ", expr->op == PBF_OP_EQ);
+ TEST_ASSERT_VAL("val is 1", expr->val == 1);
+ TEST_ASSERT_VAL("lock_name is &mmap_lock",
+ expr->lock_name && !strcmp(expr->lock_name, "&mmap_lock"));
+ clear_bpf_filter_exprs(expr_head);
+
+ TEST_ASSERT_VAL("parse !lock_held_exclusive(mmap_lock)",
+ __perf_bpf_filter__parse(expr_head,
+ "!lock_held_exclusive(mmap_lock)") == 0);
+ TEST_ASSERT_VAL("not empty", !list_empty(expr_head));
+ expr = list_first_entry(expr_head, struct perf_bpf_filter_expr, list);
+ TEST_ASSERT_VAL("term is PBF_TERM_LOCK_HELD", expr->term == PBF_TERM_LOCK_HELD);
+ TEST_ASSERT_VAL("part is PBF_LOCK_MODE_WRITE", expr->part == PBF_LOCK_MODE_WRITE);
+ TEST_ASSERT_VAL("op is PBF_OP_EQ", expr->op == PBF_OP_EQ);
+ TEST_ASSERT_VAL("val is 0", expr->val == 0);
+ TEST_ASSERT_VAL("lock_name is mmap_lock",
+ expr->lock_name && !strcmp(expr->lock_name, "mmap_lock"));
+ clear_bpf_filter_exprs(expr_head);
+
+ /*
+ * Comparison and compound group:
+ * lock_held(rwsem:R) == 1 || lock_held(&dentry) != 0, period > 100
+ */
+ TEST_ASSERT_VAL("parse compound lock_held filter",
+ __perf_bpf_filter__parse(expr_head, compound) == 0);
+ TEST_ASSERT_VAL("not empty", !list_empty(expr_head));
+ expr = list_first_entry(expr_head, struct perf_bpf_filter_expr, list);
+ TEST_ASSERT_VAL("group op", expr->op == PBF_OP_GROUP_BEGIN);
+ TEST_ASSERT_VAL("groups not empty", !list_empty(&expr->groups));
+ group = list_first_entry(&expr->groups, struct perf_bpf_filter_expr, list);
+ TEST_ASSERT_VAL("first group member lock_name",
+ group->lock_name && !strcmp(group->lock_name, "rwsem:R"));
+ TEST_ASSERT_VAL("first group member op", group->op == PBF_OP_EQ && group->val == 1);
+ group = list_last_entry(&expr->groups, struct perf_bpf_filter_expr, list);
+ TEST_ASSERT_VAL("second group member lock_name",
+ group->lock_name && !strcmp(group->lock_name, "&dentry"));
+ TEST_ASSERT_VAL("second group member op", group->op == PBF_OP_NEQ && group->val == 0);
+ expr = list_last_entry(expr_head, struct perf_bpf_filter_expr, list);
+ TEST_ASSERT_VAL("second term is period", expr->term == PBF_TERM_PERIOD && expr->val == 100);
+
+ return TEST_OK;
+}
+#endif
+
+static int test__bpf_filters(struct test_suite *test __maybe_unused,
+ int subtest __maybe_unused)
+{
+#ifdef HAVE_BPF_SKEL
+ struct list_head expr_head = LIST_HEAD_INIT(expr_head);
+ int ret = do_test_bpf_filters(&expr_head);
+
+ clear_bpf_filter_exprs(&expr_head);
+ return ret;
+#else
+ return TEST_SKIP;
+#endif
+}
+
static struct test_case tests__parse_events[] = {
TEST_CASE_REASON("Test event parsing",
events2,
@@ -2952,6 +3059,7 @@ static struct test_case tests__parse_events[] = {
"no aliases in sysfs"),
TEST_CASE("Parsing of aliased events", pmu_events_alias2),
TEST_CASE("Parsing of terms (event modifiers)", terms2),
+ TEST_CASE("Parsing of BPF sample filters", bpf_filters),
{ .name = NULL, }
};
diff --git a/tools/perf/tests/shell/record_bpf_filter.sh b/tools/perf/tests/shell/record_bpf_filter.sh
index 383574cb3bd3..adaa5363d0b7 100755
--- a/tools/perf/tests/shell/record_bpf_filter.sh
+++ b/tools/perf/tests/shell/record_bpf_filter.sh
@@ -176,6 +176,105 @@ test_bpf_filter_cgroup() {
echo "Cgroup bpf-filter test [Success]"
}
+test_bpf_filter_lock_held() {
+ echo "Lock-held bpf-filter test"
+ lock_sym="cgroup_mutex"
+
+ if [ -r /proc/kallsyms ] && ! grep -qw "${lock_sym}" /proc/kallsyms
+ then
+ lock_sym="slab_mutex"
+ fi
+
+ if ! perf record -e task-clock --filter "lock_held(&${lock_sym})" \
+ -o "${perfdata}" true 2> /dev/null
+ then
+ if [ "$(id -u)" != 0 ]
+ then
+ echo "Lock-held bpf-filter test [Skipped permission]"
+ return
+ fi
+ if [ -r /proc/kallsyms ] && ! grep -qw "${lock_sym}" /proc/kallsyms
+ then
+ echo "Lock-held bpf-filter test [Skipped missing ${lock_sym}]"
+ return
+ fi
+ echo "Lock-held bpf-filter test [Failed &${lock_sym}]"
+ err=1
+ return
+ fi
+
+ if ! perf record -e task-clock --filter "!lock_held(${lock_sym})" \
+ -o "${perfdata}" true 2> /dev/null
+ then
+ echo "Lock-held bpf-filter test [Failed !lock_held(${lock_sym})]"
+ err=1
+ return
+ fi
+
+ if ! perf record -e task-clock --filter 'lock_held(mmap_lock)' \
+ -o "${perfdata}" perf bench mem mmap -t 2 -l 10 2> /dev/null
+ then
+ echo "Lock-held bpf-filter test [Failed mmap_lock]"
+ err=1
+ return
+ fi
+
+ if ! perf record -e task-clock --filter 'lock_held(rq_lock) || lock_held(spinlock)' \
+ -o "${perfdata}" true 2> /dev/null
+ then
+ if ! perf record -e task-clock --filter 'lock_held(rq_lock) || lock_held(spinlock)' \
+ -o /dev/null true 2>&1 | grep -q "Kernel BTF lacks required"
+ then
+ echo "Lock-held bpf-filter test [Failed rq_lock/spinlock]"
+ err=1
+ return
+ fi
+ fi
+
+ if ! perf record -e task-clock --filter 'lock_held(mutex) || lock_held(rwsem)' \
+ -o "${perfdata}" true 2> /dev/null
+ then
+ echo "Lock-held bpf-filter test [Failed lock type]"
+ err=1
+ return
+ fi
+
+ if ! perf record -e task-clock \
+ --filter 'lock_held_read(&mmap_lock) || lock_held_write(&mmap_lock)' \
+ -o "${perfdata}" true 2> /dev/null
+ then
+ echo "Lock-held bpf-filter test [Failed lock_held_read/write(&mmap_lock)]"
+ err=1
+ return
+ fi
+
+ if ! perf record -e task-clock --filter 'lock_held(&mmap_lock:R) || lock_held(&mmap_lock:W)' \
+ -o "${perfdata}" true 2> /dev/null
+ then
+ echo "Lock-held bpf-filter test [Failed &mmap_lock:R/:W]"
+ err=1
+ return
+ fi
+
+ if perf record -e task-clock --filter 'lock_held_read(rq_lock)' \
+ -o /dev/null true 2> /dev/null
+ then
+ echo "Lock-held bpf-filter test [Failed lock_held_read(rq_lock) should fail]"
+ err=1
+ return
+ fi
+
+ if perf record -e task-clock --filter 'lock_held(nonexistent_lock_xyz)' \
+ -o /dev/null true 2> /dev/null
+ then
+ echo "Lock-held bpf-filter test [Failed nonexistent lock should fail]"
+ err=1
+ return
+ fi
+
+ echo "Lock-held bpf-filter test [Success]"
+}
+
test_bpf_filter_priv
if [ $err = 0 ]; then
@@ -198,5 +297,9 @@ if [ $err = 0 ]; then
test_bpf_filter_cgroup
fi
+if [ $err = 0 ]; then
+ test_bpf_filter_lock_held
+fi
+
cleanup
exit $err
--
2.56.0.212.g129ede20d9-goog
next prev parent reply other threads:[~2026-10-17 17:14 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-17 5:45 [PATCH v1 0/5] perf bpf-filter / lock contention: Add lock_held() sample filter and RCU/SRCU support Ian Rogers
2026-10-17 5:45 ` [PATCH v1 1/5] perf lock contention: Share common BPF and lock type definitions in lock_data.h Ian Rogers
2026-10-17 5:52 ` sashiko-bot
2026-10-17 5:45 ` [PATCH v1 2/5] perf bpf-filter: Add lock_held() sample filter Ian Rogers
2026-10-17 6:09 ` sashiko-bot
2026-10-17 5:45 ` [PATCH v1 3/5] perf test: Add tests for lock_held() BPF " Ian Rogers
2026-10-17 5:49 ` sashiko-bot
2026-10-17 5:45 ` [PATCH v1 4/5] perf bpf-filter: Extend lock_held() to support RCU, Tasks Trace RCU, and SRCU Ian Rogers
2026-10-17 5:56 ` sashiko-bot
2026-10-17 5:45 ` [PATCH v1 5/5] perf lock contention: Extend BPF lock contention to RCU " Ian Rogers
2026-10-17 6:08 ` sashiko-bot
2026-10-17 17:14 ` [PATCH v2 0/6] perf bpf-filter / lock contention: Add lock_held() sample filter and RCU/SRCU support Ian Rogers
2026-10-17 17:14 ` [PATCH v2 1/6] perf lock contention: Share common BPF and lock type definitions in lock_data.h Ian Rogers
2026-10-17 17:31 ` sashiko-bot
2026-10-17 17:14 ` [PATCH v2 2/6] perf bpf-filter: Add lock_held() sample filter Ian Rogers
2026-10-17 17:35 ` sashiko-bot
2026-10-17 17:14 ` Ian Rogers [this message]
2026-10-17 17:17 ` [PATCH v2 3/6] perf test: Add tests for lock_held() BPF " sashiko-bot
2026-10-17 17:14 ` [PATCH v2 4/6] perf bpf-filter: Extend lock_held() to support RCU, Tasks Trace RCU, and SRCU Ian Rogers
2026-10-17 17:25 ` sashiko-bot
2026-10-17 17:14 ` [PATCH v2 5/6] perf lock contention: Extend BPF lock contention to RCU " Ian Rogers
2026-10-17 17:30 ` sashiko-bot
2026-10-17 17:14 ` [PATCH v2 6/6] perf lock contention: Filter events before creating lock_stat Ian Rogers
2026-10-17 17:17 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261017171437.2066343-4-irogers@google.com \
--to=irogers@google.com \
--cc=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=james.clark@linaro.org \
--cc=jolsa@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=peterz@infradead.org \
--cc=rcu@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox