[tip: perf/core] perf trace: Expand strings in filters to integers

From: tip-bot2 for Arnaldo Carvalho de Melo
Date: Tue Oct 15 2019 - 01:35:59 EST


The following commit has been merged into the perf/core branch of tip:

Commit-ID: 90df0249c2eae21f329760ee857575260926188a
Gitweb: https://git.kernel.org/tip/90df0249c2eae21f329760ee857575260926188a
Author: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
AuthorDate: Wed, 09 Oct 2019 16:22:16 -03:00
Committer: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
CommitterDate: Wed, 09 Oct 2019 16:22:16 -03:00

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@xxxxxxxxx>
Cc: Brendan Gregg <brendan.d.gregg@xxxxxxxxx>
Cc: Jiri Olsa <jolsa@xxxxxxxxxx>
Cc: Luis ClÃudio GonÃalves <lclaudio@xxxxxxxxxx>
Cc: Namhyung Kim <namhyung@xxxxxxxxxx>
Link: https://lkml.kernel.org/n/tip-dx5j70fv2rgkeezd1cb3hv2p@xxxxxxxxxxxxxx
Signed-off-by: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
---
tools/perf/builtin-trace.c | 130 ++++++++++++++++++++++++++++++++++++-
1 file changed, 130 insertions(+)

diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 50a1aeb..515a800 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -3484,6 +3484,133 @@ static int ordered_events__deliver_event(struct ordered_events *oe,
return __trace__deliver_event(trace, event->event);
}

+static struct syscall_arg_fmt *perf_evsel__syscall_arg_fmt(struct evsel *evsel, char *arg)
+{
+ struct tep_format_field *field;
+ struct syscall_arg_fmt *fmt = evsel->priv;
+
+ if (evsel->tp_format == NULL || fmt == NULL)
+ return NULL;
+
+ for (field = evsel->tp_format->format.fields; field; field = field->next, ++fmt)
+ if (strcmp(field->name, arg) == 0)
+ return fmt;
+
+ return NULL;
+}
+
+static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel *evsel)
+{
+ char *tok, *left = evsel->filter, *new_filter = evsel->filter;
+
+ while ((tok = strpbrk(left, "=<>!")) != NULL) {
+ char *right = tok + 1, *right_end;
+
+ if (*right == '=')
+ ++right;
+
+ while (isspace(*right))
+ ++right;
+
+ if (*right == '\0')
+ break;
+
+ while (!isalpha(*left))
+ if (++left == tok) {
+ /*
+ * Bail out, can't find the name of the argument that is being
+ * used in the filter, let it try to set this filter, will fail later.
+ */
+ return 0;
+ }
+
+ right_end = right + 1;
+ while (isalnum(*right_end) || *right_end == '_')
+ ++right_end;
+
+ if (isalpha(*right)) {
+ struct syscall_arg_fmt *fmt;
+ int left_size = tok - left,
+ right_size = right_end - right;
+ char arg[128];
+
+ while (isspace(left[left_size - 1]))
+ --left_size;
+
+ scnprintf(arg, sizeof(arg), "%.*s", left_size, left);
+
+ fmt = perf_evsel__syscall_arg_fmt(evsel, arg);
+ if (fmt == NULL) {
+ pr_debug("\"%s\" not found in \"%s\", can't set filter \"%s\"\n",
+ arg, evsel->name, evsel->filter);
+ return -1;
+ }
+
+ pr_debug2("trying to expand \"%s\" \"%.*s\" \"%.*s\" -> ",
+ arg, (int)(right - tok), tok, right_size, right);
+
+ if (fmt->strtoul) {
+ u64 val;
+ if (fmt->strtoul(right, right_size, NULL, &val)) {
+ char *n, expansion[19];
+ int expansion_lenght = scnprintf(expansion, sizeof(expansion), "%#" PRIx64, val);
+ int expansion_offset = right - new_filter;
+
+ pr_debug("%s", expansion);
+
+ if (asprintf(&n, "%.*s%s%s", expansion_offset, new_filter, expansion, right_end) < 0) {
+ pr_debug(" out of memory!\n");
+ free(new_filter);
+ return -1;
+ }
+ if (new_filter != evsel->filter)
+ free(new_filter);
+ left = n + expansion_offset + expansion_lenght;
+ new_filter = n;
+ } else {
+ pr_err("\"%.*s\" not found for \"%s\" in \"%s\", can't set filter \"%s\"\n",
+ right_size, right, arg, evsel->name, evsel->filter);
+ return -1;
+ }
+ } else {
+ pr_err("No resolver (strtoul) for \"%s\" in \"%s\", can't set filter \"%s\"\n",
+ arg, evsel->name, evsel->filter);
+ return -1;
+ }
+
+ pr_debug("\n");
+ } else {
+ left = right_end;
+ }
+ }
+
+ if (new_filter != evsel->filter) {
+ pr_debug("New filter for %s: %s\n", evsel->name, new_filter);
+ perf_evsel__set_filter(evsel, new_filter);
+ free(new_filter);
+ }
+
+ return 0;
+}
+
+static int trace__expand_filters(struct trace *trace, struct evsel **err_evsel)
+{
+ struct evlist *evlist = trace->evlist;
+ struct evsel *evsel;
+
+ evlist__for_each_entry(evlist, evsel) {
+ if (evsel->filter == NULL)
+ continue;
+
+ if (trace__expand_filter(trace, evsel)) {
+ *err_evsel = evsel;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
static int trace__run(struct trace *trace, int argc, const char **argv)
{
struct evlist *evlist = trace->evlist;
@@ -3625,6 +3752,9 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
*/
trace->fd_path_disabled = !trace__syscall_enabled(trace, syscalltbl__id(trace->sctbl, "close"));

+ err = trace__expand_filters(trace, &evsel);
+ if (err)
+ goto out_delete_evlist;
err = perf_evlist__apply_filters(evlist, &evsel);
if (err < 0)
goto out_error_apply_filters;