perf record: Switch user option to use BPF filter
authorIan Rogers <irogers@google.com>
Wed, 4 Jun 2025 17:45:38 +0000 (10:45 -0700)
committerNamhyung Kim <namhyung@kernel.org>
Mon, 9 Jun 2025 18:18:17 +0000 (11:18 -0700)
Finding user processes by scanning /proc is inherently racy and
results in perf_event_open failures. Use a BPF filter to drop samples
where the uid doesn't match. Ensure adding the BPF filter forces
system-wide.

Signed-off-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250604174545.2853620-5-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
tools/perf/builtin-record.c

index 8059bce85a510b2dddc66f1b8b0013276840eddc..0b566f300569ab021b4ea9d2cac85cd08f42be8f 100644 (file)
@@ -175,6 +175,7 @@ struct record {
        bool                    timestamp_boundary;
        bool                    off_cpu;
        const char              *filter_action;
+       const char              *uid_str;
        struct switch_output    switch_output;
        unsigned long long      samples;
        unsigned long           output_max_size;        /* = 0: unlimited */
@@ -3513,8 +3514,7 @@ static struct option __record_options[] = {
                     "or ranges of time to enable events e.g. '-D 10-20,30-40'",
                     record__parse_event_enable_time),
        OPT_BOOLEAN(0, "kcore", &record.opts.kcore, "copy /proc/kcore"),
-       OPT_STRING('u', "uid", &record.opts.target.uid_str, "user",
-                  "user to profile"),
+       OPT_STRING('u', "uid", &record.uid_str, "user", "user to profile"),
 
        OPT_CALLBACK_NOOPT('b', "branch-any", &record.opts.branch_stack,
                     "branch any", "sample any taken branches",
@@ -4256,19 +4256,24 @@ int cmd_record(int argc, const char **argv)
                ui__warning("%s\n", errbuf);
        }
 
-       err = target__parse_uid(&rec->opts.target);
-       if (err) {
-               int saved_errno = errno;
+       if (rec->uid_str) {
+               uid_t uid = parse_uid(rec->uid_str);
 
-               target__strerror(&rec->opts.target, err, errbuf, BUFSIZ);
-               ui__error("%s", errbuf);
+               if (uid == UINT_MAX) {
+                       ui__error("Invalid User: %s", rec->uid_str);
+                       err = -EINVAL;
+                       goto out;
+               }
+               err = parse_uid_filter(rec->evlist, uid);
+               if (err)
+                       goto out;
 
-               err = -saved_errno;
-               goto out;
+               /* User ID filtering implies system wide. */
+               rec->opts.target.system_wide = true;
        }
 
-       /* Enable ignoring missing threads when -u/-p option is defined. */
-       rec->opts.ignore_missing_thread = rec->opts.target.uid != UINT_MAX || rec->opts.target.pid;
+       /* Enable ignoring missing threads when -p option is defined. */
+       rec->opts.ignore_missing_thread = rec->opts.target.pid;
 
        evlist__warn_user_requested_cpus(rec->evlist, rec->opts.target.cpu_list);