Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf trace: Expand strings in filters to integers

So that one can try things like:

# perf trace -e msr:* --filter="msr!=FS_BASE && msr != IA32_TSC_DEADLINE && msr != 0x830 && msr != 0x83f && msr !=IA32_SPEC_CTRL" --filter-pids 3750

That, at this point in the patchset, without any strtoul in place for
tracepoint arguments, will result in:

No resolver (strtoul) for "msr" in "msr:read_msr", can't set filter "(msr!=FS_BASE && msr != IA32_TSC_DEADLINE && msr != 0x830 && msr != 0x83f && msr !=IA32_SPEC_CTRL) && (common_pid != 25407 && common_pid != 3750)"
#

See you in the next cset!

Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Luis Cláudio Gonçalves <lclaudio@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: https://lkml.kernel.org/n/tip-dx5j70fv2rgkeezd1cb3hv2p@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

+130
+130
tools/perf/builtin-trace.c
··· 3484 3484 return __trace__deliver_event(trace, event->event); 3485 3485 } 3486 3486 3487 + static struct syscall_arg_fmt *perf_evsel__syscall_arg_fmt(struct evsel *evsel, char *arg) 3488 + { 3489 + struct tep_format_field *field; 3490 + struct syscall_arg_fmt *fmt = evsel->priv; 3491 + 3492 + if (evsel->tp_format == NULL || fmt == NULL) 3493 + return NULL; 3494 + 3495 + for (field = evsel->tp_format->format.fields; field; field = field->next, ++fmt) 3496 + if (strcmp(field->name, arg) == 0) 3497 + return fmt; 3498 + 3499 + return NULL; 3500 + } 3501 + 3502 + static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel *evsel) 3503 + { 3504 + char *tok, *left = evsel->filter, *new_filter = evsel->filter; 3505 + 3506 + while ((tok = strpbrk(left, "=<>!")) != NULL) { 3507 + char *right = tok + 1, *right_end; 3508 + 3509 + if (*right == '=') 3510 + ++right; 3511 + 3512 + while (isspace(*right)) 3513 + ++right; 3514 + 3515 + if (*right == '\0') 3516 + break; 3517 + 3518 + while (!isalpha(*left)) 3519 + if (++left == tok) { 3520 + /* 3521 + * Bail out, can't find the name of the argument that is being 3522 + * used in the filter, let it try to set this filter, will fail later. 3523 + */ 3524 + return 0; 3525 + } 3526 + 3527 + right_end = right + 1; 3528 + while (isalnum(*right_end) || *right_end == '_') 3529 + ++right_end; 3530 + 3531 + if (isalpha(*right)) { 3532 + struct syscall_arg_fmt *fmt; 3533 + int left_size = tok - left, 3534 + right_size = right_end - right; 3535 + char arg[128]; 3536 + 3537 + while (isspace(left[left_size - 1])) 3538 + --left_size; 3539 + 3540 + scnprintf(arg, sizeof(arg), "%.*s", left_size, left); 3541 + 3542 + fmt = perf_evsel__syscall_arg_fmt(evsel, arg); 3543 + if (fmt == NULL) { 3544 + pr_debug("\"%s\" not found in \"%s\", can't set filter \"%s\"\n", 3545 + arg, evsel->name, evsel->filter); 3546 + return -1; 3547 + } 3548 + 3549 + pr_debug2("trying to expand \"%s\" \"%.*s\" \"%.*s\" -> ", 3550 + arg, (int)(right - tok), tok, right_size, right); 3551 + 3552 + if (fmt->strtoul) { 3553 + u64 val; 3554 + if (fmt->strtoul(right, right_size, NULL, &val)) { 3555 + char *n, expansion[19]; 3556 + int expansion_lenght = scnprintf(expansion, sizeof(expansion), "%#" PRIx64, val); 3557 + int expansion_offset = right - new_filter; 3558 + 3559 + pr_debug("%s", expansion); 3560 + 3561 + if (asprintf(&n, "%.*s%s%s", expansion_offset, new_filter, expansion, right_end) < 0) { 3562 + pr_debug(" out of memory!\n"); 3563 + free(new_filter); 3564 + return -1; 3565 + } 3566 + if (new_filter != evsel->filter) 3567 + free(new_filter); 3568 + left = n + expansion_offset + expansion_lenght; 3569 + new_filter = n; 3570 + } else { 3571 + pr_err("\"%.*s\" not found for \"%s\" in \"%s\", can't set filter \"%s\"\n", 3572 + right_size, right, arg, evsel->name, evsel->filter); 3573 + return -1; 3574 + } 3575 + } else { 3576 + pr_err("No resolver (strtoul) for \"%s\" in \"%s\", can't set filter \"%s\"\n", 3577 + arg, evsel->name, evsel->filter); 3578 + return -1; 3579 + } 3580 + 3581 + pr_debug("\n"); 3582 + } else { 3583 + left = right_end; 3584 + } 3585 + } 3586 + 3587 + if (new_filter != evsel->filter) { 3588 + pr_debug("New filter for %s: %s\n", evsel->name, new_filter); 3589 + perf_evsel__set_filter(evsel, new_filter); 3590 + free(new_filter); 3591 + } 3592 + 3593 + return 0; 3594 + } 3595 + 3596 + static int trace__expand_filters(struct trace *trace, struct evsel **err_evsel) 3597 + { 3598 + struct evlist *evlist = trace->evlist; 3599 + struct evsel *evsel; 3600 + 3601 + evlist__for_each_entry(evlist, evsel) { 3602 + if (evsel->filter == NULL) 3603 + continue; 3604 + 3605 + if (trace__expand_filter(trace, evsel)) { 3606 + *err_evsel = evsel; 3607 + return -1; 3608 + } 3609 + } 3610 + 3611 + return 0; 3612 + } 3613 + 3487 3614 static int trace__run(struct trace *trace, int argc, const char **argv) 3488 3615 { 3489 3616 struct evlist *evlist = trace->evlist; ··· 3752 3625 */ 3753 3626 trace->fd_path_disabled = !trace__syscall_enabled(trace, syscalltbl__id(trace->sctbl, "close")); 3754 3627 3628 + err = trace__expand_filters(trace, &evsel); 3629 + if (err) 3630 + goto out_delete_evlist; 3755 3631 err = perf_evlist__apply_filters(evlist, &evsel); 3756 3632 if (err < 0) 3757 3633 goto out_error_apply_filters;