linux-perf-users.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC 00/12] perf record: Add event action support
@ 2024-11-28 13:35 Yang Jihong
  2024-11-28 13:35 ` [RFC 01/12] " Yang Jihong
                   ` (13 more replies)
  0 siblings, 14 replies; 27+ messages in thread
From: Yang Jihong @ 2024-11-28 13:35 UTC (permalink / raw)
  To: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
	jolsa, irogers, adrian.hunter, kan.liang, james.clark,
	linux-perf-users, linux-kernel
  Cc: yangjihong

In perf-record, when an event is triggered, default behavior is to
save sample data to perf.data. Sometimes, we may just want to do
some lightweight actions, such as printing a log.
    
Based on this requirement, add the --action option to the event to
specify the behavior when the event occurs.

This patchset uses bpf prog to attach to tracepoint event, and save sample
to bpf perf_event ringbuffer in handler. perf-tool read the data and run actions.

Currently only one call is supported, that is, print(),
and some commonly used builtin variables are also supported.

For example:

  # perf record -e sched:sched_switch --action 'print("[%03d][%llu]comm=%s, pid=%d, tid=%d\n", cpu, time, comm, pid, tid)' true
  [003][795464100275136]comm=perf, pid=141580, tid=141580
  [003][795464100278234]comm=swapper/3, pid=0, tid=0
  [003][795464100288984]comm=perf, pid=141580, tid=141580
  [003][795464100457865]comm=swapper/3, pid=0, tid=0
  [003][795464100485547]comm=perf, pid=141580, tid=141580
  [003][795464100491398]comm=kworker/u36:1, pid=139834, tid=139834
  [003][795464100493647]comm=perf, pid=141580, tid=141580
  [003][795464100494967]comm=kworker/u36:1, pid=139834, tid=139834
  [003][795464100498146]comm=perf, pid=141580, tid=141580
  ...

  # perf record -e cycles --action 'print("test\n");' true
  bpf record action only supports specifying for tracepoint tracer

  # perf record -e sched:sched_switch --action 'print("[%llu]comm=%s, cpu=%d, pid=%d, tid=%d\n", time, comm, cpu, pid)' true
  print() arguments number for format string mismatch: 5 expected, 4 provided
  parse action option failed

   Usage: perf record [<options>] [<command>]
      or: perf record [<options>] -- <command> [<options>]

          --action <action>
                            event action

  # perf record -e sched:sched_switch --action 'print("test\n");' true
  test
  test
  test
  test
  test
  test
  test
  test
  test
  test
  ...

This patchset implements simple features and can be extended as needed.

TODO LIST:
1. Support common operations such as logical operations and bit operations
2. Support other calls such as dumpstack(), count()
3. Support specify actions for kprobe events
4. For builds that disable bpf_skel, support real-time parsing of perf record mmap ringbuffer data (similar to perf top)
5. Link libllvm to support dynamic generation of bpf progs

Yang Jihong (12):
  perf record: Add event action support
  perf event action: Add parsing const expr support
  perf event action: Add parsing const integer expr support
  perf event action: Add parsing const string expr support
  perf event action: Add parsing call expr support
  perf event action: Add parsing print() call expr support
  perf event action: Add parsing builtin expr support
  perf event action: Add parsing builtin cpu expr support
  perf event action: Add parsing builtin pid expr support
  perf event action: Add parsing builtin tid expr support
  perf event action: Add parsing builtin comm expr support
  perf event action: Add parsing builtin time expr support

 tools/perf/Documentation/perf-record.txt     |   8 +
 tools/perf/Makefile.perf                     |   1 +
 tools/perf/builtin-record.c                  |  31 +
 tools/perf/util/Build                        |  18 +
 tools/perf/util/bpf_skel/bpf_record_action.h |  24 +
 tools/perf/util/bpf_skel/record_action.bpf.c | 151 ++++
 tools/perf/util/parse-action.c               | 729 +++++++++++++++++++
 tools/perf/util/parse-action.h               |  98 +++
 tools/perf/util/parse-action.l               | 190 +++++
 tools/perf/util/parse-action.y               | 156 ++++
 tools/perf/util/record_action.c              | 380 ++++++++++
 tools/perf/util/record_action.h              |  30 +
 12 files changed, 1816 insertions(+)
 create mode 100644 tools/perf/util/bpf_skel/bpf_record_action.h
 create mode 100644 tools/perf/util/bpf_skel/record_action.bpf.c
 create mode 100644 tools/perf/util/parse-action.c
 create mode 100644 tools/perf/util/parse-action.h
 create mode 100644 tools/perf/util/parse-action.l
 create mode 100644 tools/perf/util/parse-action.y
 create mode 100644 tools/perf/util/record_action.c
 create mode 100644 tools/perf/util/record_action.h

-- 
2.25.1


^ permalink raw reply	[flat|nested] 27+ messages in thread

* [RFC 01/12] perf record: Add event action support
  2024-11-28 13:35 [RFC 00/12] perf record: Add event action support Yang Jihong
@ 2024-11-28 13:35 ` Yang Jihong
  2024-11-28 20:19   ` Arnaldo Carvalho de Melo
  2024-11-28 13:35 ` [RFC 02/12] perf event action: Add parsing const expr support Yang Jihong
                   ` (12 subsequent siblings)
  13 siblings, 1 reply; 27+ messages in thread
From: Yang Jihong @ 2024-11-28 13:35 UTC (permalink / raw)
  To: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
	jolsa, irogers, adrian.hunter, kan.liang, james.clark,
	linux-perf-users, linux-kernel
  Cc: yangjihong

In perf-record, when an event is triggered, default behavior is to
save sample data to perf.data. Sometimes, we may just want to do
some lightweight actions, such as printing a log.

Based on this requirement, add the --action option to the event to
specify the behavior when the event occurs.

Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
---
 tools/perf/Documentation/perf-record.txt |   8 +
 tools/perf/builtin-record.c              |  31 +++
 tools/perf/util/Build                    |  18 ++
 tools/perf/util/parse-action.c           | 230 +++++++++++++++++++++++
 tools/perf/util/parse-action.h           |  75 ++++++++
 tools/perf/util/parse-action.l           |  40 ++++
 tools/perf/util/parse-action.y           |  82 ++++++++
 tools/perf/util/record_action.c          |  15 ++
 tools/perf/util/record_action.h          |  24 +++
 9 files changed, 523 insertions(+)
 create mode 100644 tools/perf/util/parse-action.c
 create mode 100644 tools/perf/util/parse-action.h
 create mode 100644 tools/perf/util/parse-action.l
 create mode 100644 tools/perf/util/parse-action.y
 create mode 100644 tools/perf/util/record_action.c
 create mode 100644 tools/perf/util/record_action.h

diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index 242223240a08..d0d9e0f69f3d 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -833,6 +833,14 @@ filtered through the mask provided by -C option.
 	Prepare BPF filter to be used by regular users.  The action should be
 	either "pin" or "unpin".  The filter can be used after it's pinned.
 
+--action=<action>::
+	Actions are the programs that run when the sampling event is triggered.
+	The action is a list of expressions separated by semicolons (;).
+	The sample data is saved by bpf prog attached by the event.
+	The call currently supported is print(); some commonly used built-in special
+	variables are also supported
+	For example:
+	  # perf record -e sched:sched_switch --action 'print("[%llu]comm=%s, cpu=%d, pid=%d, tid=%d\n", time, comm, cpu, pid, tid)' true
 
 include::intel-hybrid.txt[]
 
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index f83252472921..108d98706873 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -51,6 +51,10 @@
 #include "util/clockid.h"
 #include "util/off_cpu.h"
 #include "util/bpf-filter.h"
+#ifdef HAVE_BPF_SKEL
+#include "util/parse-action.h"
+#include "util/record_action.h"
+#endif
 #include "asm/bug.h"
 #include "perf.h"
 #include "cputopo.h"
@@ -182,6 +186,7 @@ struct record {
 	struct pollfd_index_map	*index_map;
 	size_t			index_map_sz;
 	size_t			index_map_cnt;
+	bool			custom_action;
 };
 
 static volatile int done;
@@ -3316,6 +3321,23 @@ static int parse_record_synth_option(const struct option *opt,
 	return 0;
 }
 
+#ifdef HAVE_BPF_SKEL
+static int parse_record_action_option(const struct option *opt,
+				      const char *str,
+				      int unset __maybe_unused)
+{
+	int ret;
+	struct record *rec = (struct record *)opt->value;
+
+	ret = parse_record_action(rec->evlist, str);
+	if (ret)
+		return ret;
+
+	rec->custom_action = true;
+	return 0;
+}
+#endif
+
 /*
  * XXX Ideally would be local to cmd_record() and passed to a record__new
  * because we need to have access to it in record__exit, that is called
@@ -3564,6 +3586,9 @@ static struct option __record_options[] = {
 	OPT_BOOLEAN(0, "off-cpu", &record.off_cpu, "Enable off-cpu analysis"),
 	OPT_STRING(0, "setup-filter", &record.filter_action, "pin|unpin",
 		   "BPF filter action"),
+#ifdef HAVE_BPF_SKEL
+	OPT_CALLBACK(0, "action", &record, "action", "event action", parse_record_action_option),
+#endif
 	OPT_END()
 };
 
@@ -4001,6 +4026,12 @@ int cmd_record(int argc, const char **argv)
 	if (quiet)
 		perf_quiet_option();
 
+#ifdef HAVE_BPF_SKEL
+	/* Currently, event actions only supported using bpf prog. */
+	if (rec->custom_action)
+		return bpf_perf_record(rec->evlist, argc, argv);
+#endif
+
 	err = symbol__validate_sym_arguments();
 	if (err)
 		return err;
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index c06d2ee9024c..db4c4cabc5f8 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -249,6 +249,12 @@ perf-util-$(CONFIG_LIBBPF) += bpf-utils.o
 
 perf-util-$(CONFIG_LIBPFM4) += pfm.o
 
+# perf record event action
+perf-util-$(CONFIG_PERF_BPF_SKEL) += parse-action.o
+perf-util-$(CONFIG_PERF_BPF_SKEL) += parse-action-flex.o
+perf-util-$(CONFIG_PERF_BPF_SKEL) += parse-action-bison.o
+perf-util-$(CONFIG_PERF_BPF_SKEL) += record_action.o
+
 CFLAGS_config.o   += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
 
 # avoid compiler warnings in 32-bit mode
@@ -294,6 +300,16 @@ $(OUTPUT)util/bpf-filter-bison.c $(OUTPUT)util/bpf-filter-bison.h: util/bpf-filt
 	$(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) $(BISON_FILE_PREFIX_MAP) \
 		-o $(OUTPUT)util/bpf-filter-bison.c -p perf_bpf_filter_
 
+$(OUTPUT)util/parse-action-flex.c $(OUTPUT)util/parse-action-flex.h: util/parse-action.l $(OUTPUT)util/parse-action-bison.c util/parse-action.h
+	$(call rule_mkdir)
+	$(Q)$(call echo-cmd,flex)$(FLEX) -o $(OUTPUT)util/parse-action-flex.c \
+		--header-file=$(OUTPUT)util/parse-action-flex.h $(PARSER_DEBUG_FLEX) $<
+
+$(OUTPUT)util/parse-action.c $(OUTPUT)util/parse-action-bison.h: util/parse-action.y util/parse-action.h
+	$(call rule_mkdir)
+	$(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) $(BISON_FILE_PREFIX_MAP) \
+		-o $(OUTPUT)util/parse-action-bison.c -p parse_action_
+
 FLEX_VERSION := $(shell $(FLEX) --version | cut -d' ' -f2)
 
 FLEX_GE_260 := $(call version-ge3,$(FLEX_VERSION),2.6.0)
@@ -345,11 +361,13 @@ CFLAGS_parse-events-flex.o  += $(flex_flags) -Wno-unused-label
 CFLAGS_pmu-flex.o           += $(flex_flags)
 CFLAGS_expr-flex.o          += $(flex_flags)
 CFLAGS_bpf-filter-flex.o    += $(flex_flags)
+CFLAGS_parse-action-flex.o  += $(flex_flags)
 
 CFLAGS_parse-events-bison.o += $(bison_flags)
 CFLAGS_pmu-bison.o          += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
 CFLAGS_expr-bison.o         += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
 CFLAGS_bpf-filter-bison.o   += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
+CFLAGS_parse-action-bison.o += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
 
 $(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c
 $(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c
diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
new file mode 100644
index 000000000000..01c8c7fdea59
--- /dev/null
+++ b/tools/perf/util/parse-action.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * Generic actions for sampling events
+ * Actions are the programs that run when the sampling event is triggered.
+ * The action is a list of expressions separated by semicolons (;).
+ * Each action is an expression, added to actions_head node as list_head node.
+ */
+
+#include "util/debug.h"
+#include "util/parse-action.h"
+#include "util/parse-action-flex.h"
+#include "util/parse-action-bison.h"
+
+static struct list_head actions_head = LIST_HEAD_INIT(actions_head);
+
+int event_actions__for_each_expr(int (*func)(struct evtact_expr *, void *arg),
+				 void *arg, bool recursive)
+{
+	int ret;
+	struct evtact_expr *expr, *opnd;
+
+	if (list_empty(&actions_head))
+		return (*func)(NULL, arg);
+
+	list_for_each_entry(expr, &actions_head, list) {
+		ret = (*func)(expr, arg);
+		if (ret)
+			return ret;
+
+		if (recursive && !list_empty(&expr->opnds)) {
+			list_for_each_entry(opnd, &expr->opnds, list) {
+				ret = (*func)(opnd, arg);
+				if (ret)
+					return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+int event_actions__for_each_expr_safe(int (*func)(struct evtact_expr *, void *arg),
+				      void *arg, bool recursive)
+{
+	int ret;
+	struct evtact_expr *expr, *tmp;
+	struct evtact_expr *opnd, *opnd_tmp;
+
+	if (list_empty(&actions_head))
+		return (*func)(NULL, arg);
+
+	list_for_each_entry_safe(expr, tmp, &actions_head, list) {
+		ret = (*func)(expr, arg);
+		if (ret)
+			return ret;
+
+		if (recursive && !list_empty(&expr->opnds)) {
+			list_for_each_entry_safe(opnd, opnd_tmp, &expr->opnds, list) {
+				ret = (*func)(opnd, arg);
+				if (ret)
+					return ret;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int parse_action_option(const char *str)
+{
+	int ret;
+	YY_BUFFER_STATE buffer;
+
+	buffer = parse_action__scan_string(str);
+	ret = parse_action_parse(&actions_head);
+
+	parse_action__flush_buffer(buffer);
+	parse_action__delete_buffer(buffer);
+	parse_action_lex_destroy();
+
+	return ret;
+}
+
+int parse_record_action(struct evlist *evlist, const char *str)
+{
+	int ret;
+
+	if (evlist == NULL) {
+		pr_err("--action option should follow a tracer option\n");
+		return -1;
+	}
+
+	ret = parse_action_option(str);
+	if (ret) {
+		event_actions__free();
+		pr_err("parse action option failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int do_action_free(struct evtact_expr *action, void *data __maybe_unused)
+{
+	if (action == NULL)
+		return 0;
+
+	list_del(&action->list);
+	parse_action_expr__free(action);
+	return 0;
+}
+
+void event_actions__free(void)
+{
+	(void)event_actions__for_each_expr_safe(do_action_free, NULL, false);
+}
+
+static struct evtact_expr_class *expr_class_list[EVTACT_EXPR_TYPE_MAX] = {
+};
+
+int parse_action_expr__set_class(enum evtact_expr_type type,
+				 struct evtact_expr_class *class)
+{
+	if (type >= EVTACT_EXPR_TYPE_MAX) {
+		pr_err("action expr set class ops type invalid\n");
+		return -EINVAL;
+	}
+
+	if (expr_class_list[type] != NULL) {
+		pr_err("action expr set class ops type already exists\n");
+		return -EEXIST;
+	}
+
+	expr_class_list[type] = class;
+	return 0;
+}
+
+static int expr_set_type(struct evtact_expr *expr)
+{
+	u64 id;
+	int ret;
+	u32 type, opcode;
+	struct evtact_expr_class *class;
+
+	id = expr->id;
+	evtact_expr_id_decode(id, &type, &opcode);
+
+	if (type >= EVTACT_EXPR_TYPE_MAX) {
+		pr_err("parse_action_expr type invalid: %u\n", type);
+		return -EINVAL;
+	}
+
+	class = expr_class_list[type];
+	if (class == NULL) {
+		pr_err("parse_action_expr class not supported: %u\n", type);
+		return -ENOTSUP;
+	}
+
+	if (class->set_ops != NULL) {
+		ret = class->set_ops(expr, opcode);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+struct evtact_expr *parse_action_expr__new(u64 id, struct list_head *opnds,
+					   void *data, int size)
+{
+	int ret;
+	struct evtact_expr *expr;
+
+	expr = malloc(sizeof(*expr));
+	if (expr == NULL) {
+		pr_err("parse_action_expr malloc failed\n");
+		goto out_free_opnds;
+	}
+	expr->id = id;
+
+	if (opnds != NULL)
+		list_add_tail(&expr->opnds, opnds);
+	else
+		INIT_LIST_HEAD(&expr->opnds);
+
+	ret = expr_set_type(expr);
+	if (ret)
+		goto out_list_del_opnds;
+
+	if (expr->ops->new != NULL) {
+		ret = expr->ops->new(expr, data, size);
+		if (ret)
+			goto out_free_expr;
+	}
+
+	return expr;
+
+out_free_expr:
+	free(expr);
+out_list_del_opnds:
+	list_del(&expr->opnds);
+out_free_opnds:
+	parse_action_expr__free_opnds(opnds);
+
+	return NULL;
+}
+
+void parse_action_expr__free_opnds(struct list_head *opnds)
+{
+	struct evtact_expr *opnd, *tmp;
+
+	if (opnds != NULL && !list_empty(opnds)) {
+		list_for_each_entry_safe(opnd, tmp, opnds, list) {
+			list_del(&opnd->list);
+			parse_action_expr__free(opnd);
+		}
+	}
+}
+
+void parse_action_expr__free(struct evtact_expr *expr)
+{
+	if (expr == NULL)
+		return;
+
+	if (expr->ops->free != NULL)
+		expr->ops->free(expr);
+
+	parse_action_expr__free_opnds(&expr->opnds);
+	free(expr);
+}
diff --git a/tools/perf/util/parse-action.h b/tools/perf/util/parse-action.h
new file mode 100644
index 000000000000..71a0a166959e
--- /dev/null
+++ b/tools/perf/util/parse-action.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PERF_UTIL_PARSE_ACTION_H_
+#define __PERF_UTIL_PARSE_ACTION_H_
+
+#include <linux/types.h>
+
+#include <subcmd/parse-options.h>
+
+#include "evlist.h"
+
+enum evtact_expr_type {
+	EVTACT_EXPR_TYPE_MAX,
+};
+
+struct evtact_expr;
+struct evtact_expr_ops {
+	int (*new)(struct evtact_expr *expr, void *data, int size);
+	int (*eval)(struct evtact_expr *expr,
+		    void *in, int in_size, void **out, int *out_size);
+	void (*free)(struct evtact_expr *expr);
+};
+
+struct evtact_expr_class {
+	int (*set_ops)(struct evtact_expr *expr, u32 opcode);
+};
+
+struct evtact_expr {
+	struct list_head list;
+	u64 id;
+	struct evtact_expr_ops *ops;
+	struct list_head opnds;
+	void *priv;
+};
+
+/*
+ * The expr id contains two fileds:
+ * |--------------|----------------|
+ * |     type     |     opcode     |
+ * |--------------|----------------|
+ *      32-bit           32-bit
+ */
+#define EVTACT_EXPR_ID_TYPE_BITS_SHIFT 32
+static inline u64 evtact_expr_id_encode(u32 type, u32 opcode)
+{
+	return (u64)type << EVTACT_EXPR_ID_TYPE_BITS_SHIFT | opcode;
+}
+
+static inline void evtact_expr_id_decode(u64 id, u32 *type, u32 *opcode)
+{
+	if (type != NULL)
+		*type = id >> EVTACT_EXPR_ID_TYPE_BITS_SHIFT;
+
+	if (opcode != NULL)
+		*opcode = id & GENMASK(EVTACT_EXPR_ID_TYPE_BITS_SHIFT, 0);
+}
+
+int parse_record_action(struct evlist *evlist, const char *str);
+void event_actions__free(void);
+
+int event_actions__for_each_expr(int (*func)(struct evtact_expr *, void *arg),
+				 void *arg, bool recursive);
+
+int event_actions__for_each_expr_safe(int (*func)(struct evtact_expr *, void *arg),
+				      void *arg, bool recursive);
+
+struct evtact_expr *parse_action_expr__new(u64 id, struct list_head *opnds,
+					   void *data, int size);
+
+void parse_action_expr__free_opnds(struct list_head *opnds);
+void parse_action_expr__free(struct evtact_expr *expr);
+
+int parse_action_expr__set_class(enum evtact_expr_type type,
+				 struct evtact_expr_class *ops);
+
+#endif /* __PERF_UTIL_PARSE_ACTION_H_ */
diff --git a/tools/perf/util/parse-action.l b/tools/perf/util/parse-action.l
new file mode 100644
index 000000000000..3cb72de50372
--- /dev/null
+++ b/tools/perf/util/parse-action.l
@@ -0,0 +1,40 @@
+%option prefix="parse_action_"
+%option noyywrap
+%option stack
+
+%{
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util/debug.h"
+
+#include "parse-action.h"
+#include "parse-action-bison.h"
+
+%}
+
+space		[ \t]
+ident		[_a-zA-Z][_a-zA-Z0-9]*
+
+%%
+
+{space}		{ }
+
+";"		{ return SEMI; }
+
+{ident}		{
+			parse_action_lval.str = strdup(parse_action_text);
+			if (parse_action_lval.str == NULL) {
+				pr_err("parse_action malloc ident string failed\n");
+				return ERROR;
+			}
+			return IDENT;
+		}
+.		{
+			pr_err("invalid character: '%s'\n", parse_action_text);
+			return ERROR;
+		}
+
+%%
diff --git a/tools/perf/util/parse-action.y b/tools/perf/util/parse-action.y
new file mode 100644
index 000000000000..fade9d093d4a
--- /dev/null
+++ b/tools/perf/util/parse-action.y
@@ -0,0 +1,82 @@
+%parse-param {struct list_head *actions_head}
+%define parse.error verbose
+
+%{
+
+#ifndef NDEBUG
+#define YYDEBUG 1
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <linux/compiler.h>
+#include <linux/list.h>
+
+#include "util/debug.h"
+#include "util/parse-action.h"
+
+int parse_action_lex(void);
+
+static void parse_action_error(struct list_head *expr __maybe_unused,
+			       char const *msg)
+{
+	pr_err("parse_action: %s\n", msg);
+}
+
+%}
+
+%union
+{
+	char *str;
+	struct evtact_expr *expr;
+	struct list_head *list;
+}
+
+%token IDENT ERROR
+%token SEMI
+%type <expr> action_term expr_term
+%destructor { parse_action_expr__free($$); } <expr>
+%type <str> IDENT
+
+%%
+
+actions:
+action_term SEMI actions
+{
+	list_add(&$1->list, actions_head);
+}
+|
+action_term SEMI
+{
+	list_add(&$1->list, actions_head);
+}
+|
+action_term
+{
+	list_add(&$1->list, actions_head);
+}
+
+action_term:
+expr_term
+{
+	$$ = $1;
+}
+
+expr_term:
+IDENT
+{
+	$$ = NULL;
+	pr_err("unsupported ident: '%s'\n", $1);
+	free($1);
+	YYERROR;
+}
+|
+ERROR
+{
+	$$ = NULL;
+	YYERROR;
+}
+
+%%
diff --git a/tools/perf/util/record_action.c b/tools/perf/util/record_action.c
new file mode 100644
index 000000000000..44789e0d4678
--- /dev/null
+++ b/tools/perf/util/record_action.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * Read event sample data and execute the specified actions.
+ */
+
+#include "util/debug.h"
+#include "util/parse-action.h"
+#include "util/record_action.h"
+
+int bpf_perf_record(struct evlist *evlist __maybe_unused,
+		    int argc __maybe_unused, const char **argv __maybe_unused)
+{
+	event_actions__free();
+	return 0;
+}
diff --git a/tools/perf/util/record_action.h b/tools/perf/util/record_action.h
new file mode 100644
index 000000000000..289be4befa97
--- /dev/null
+++ b/tools/perf/util/record_action.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PERF_UTIL_RECORD_ACTION_H_
+#define __PERF_UTIL_RECORD_ACTION_H_
+
+#include <errno.h>
+#include "evlist.h"
+
+#ifdef HAVE_BPF_SKEL
+
+int bpf_perf_record(struct evlist *evlist, int argc, const char **argv);
+
+
+#else /* !HAVE_BPF_SKEL */
+
+static inline int bpf_perf_record(struct evlist *evlist __maybe_unused,
+				  int argc __maybe_unused,
+				  const char **argv __maybe_unused)
+{
+	return -EOPNOTSUPP;
+}
+
+#endif /* !HAVE_BPF_SKEL */
+
+#endif /* __PERF_UTIL_RECORD_ACTION_H_ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [RFC 02/12] perf event action: Add parsing const expr support
  2024-11-28 13:35 [RFC 00/12] perf record: Add event action support Yang Jihong
  2024-11-28 13:35 ` [RFC 01/12] " Yang Jihong
@ 2024-11-28 13:35 ` Yang Jihong
  2024-11-28 20:23   ` Arnaldo Carvalho de Melo
  2024-11-28 13:35 ` [RFC 03/12] perf event action: Add parsing const integer " Yang Jihong
                   ` (11 subsequent siblings)
  13 siblings, 1 reply; 27+ messages in thread
From: Yang Jihong @ 2024-11-28 13:35 UTC (permalink / raw)
  To: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
	jolsa, irogers, adrian.hunter, kan.liang, james.clark,
	linux-perf-users, linux-kernel
  Cc: yangjihong

Event action requires constant expression parsing support,
which include constant integer and constant string.

Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
---
 tools/perf/util/parse-action.c | 27 +++++++++++++++++++++++++++
 tools/perf/util/parse-action.h |  5 +++++
 2 files changed, 32 insertions(+)

diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
index 01c8c7fdea59..391546bf3d73 100644
--- a/tools/perf/util/parse-action.c
+++ b/tools/perf/util/parse-action.c
@@ -4,6 +4,9 @@
  * Actions are the programs that run when the sampling event is triggered.
  * The action is a list of expressions separated by semicolons (;).
  * Each action is an expression, added to actions_head node as list_head node.
+ *
+ * Supported expressions:
+ *   - constant:
  */
 
 #include "util/debug.h"
@@ -115,7 +118,31 @@ void event_actions__free(void)
 	(void)event_actions__for_each_expr_safe(do_action_free, NULL, false);
 }
 
+static struct evtact_expr_ops *expr_const_ops_list[EVTACT_EXPR_CONST_TYPE_MAX] = {
+};
+
+static int expr_const_set_ops(struct evtact_expr *expr, u32 opcode)
+{
+	if (opcode >= EVTACT_EXPR_CONST_TYPE_MAX) {
+		pr_err("expr_const opcode invalid: %u\n", opcode);
+		return -EINVAL;
+	}
+
+	if (expr_const_ops_list[opcode] == NULL) {
+		pr_err("expr_const opcode not supported: %u\n", opcode);
+		return -ENOTSUP;
+	}
+
+	expr->ops = expr_const_ops_list[opcode];
+	return 0;
+}
+
+static struct evtact_expr_class expr_const = {
+	.set_ops = expr_const_set_ops,
+};
+
 static struct evtact_expr_class *expr_class_list[EVTACT_EXPR_TYPE_MAX] = {
+	[EVTACT_EXPR_TYPE_CONST]   = &expr_const,
 };
 
 int parse_action_expr__set_class(enum evtact_expr_type type,
diff --git a/tools/perf/util/parse-action.h b/tools/perf/util/parse-action.h
index 71a0a166959e..47bd75264dee 100644
--- a/tools/perf/util/parse-action.h
+++ b/tools/perf/util/parse-action.h
@@ -9,9 +9,14 @@
 #include "evlist.h"
 
 enum evtact_expr_type {
+	EVTACT_EXPR_TYPE_CONST,
 	EVTACT_EXPR_TYPE_MAX,
 };
 
+enum evtact_expr_const_type {
+	EVTACT_EXPR_CONST_TYPE_MAX,
+};
+
 struct evtact_expr;
 struct evtact_expr_ops {
 	int (*new)(struct evtact_expr *expr, void *data, int size);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [RFC 03/12] perf event action: Add parsing const integer expr support
  2024-11-28 13:35 [RFC 00/12] perf record: Add event action support Yang Jihong
  2024-11-28 13:35 ` [RFC 01/12] " Yang Jihong
  2024-11-28 13:35 ` [RFC 02/12] perf event action: Add parsing const expr support Yang Jihong
@ 2024-11-28 13:35 ` Yang Jihong
  2024-11-28 20:25   ` Arnaldo Carvalho de Melo
  2024-11-28 13:35 ` [RFC 04/12] perf event action: Add parsing const string " Yang Jihong
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 27+ messages in thread
From: Yang Jihong @ 2024-11-28 13:35 UTC (permalink / raw)
  To: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
	jolsa, irogers, adrian.hunter, kan.liang, james.clark,
	linux-perf-users, linux-kernel
  Cc: yangjihong

Support parsing of constant integer expression.

Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
---
 tools/perf/util/parse-action.c | 52 ++++++++++++++++++++++++++++++++++
 tools/perf/util/parse-action.h |  1 +
 tools/perf/util/parse-action.l | 19 +++++++++++++
 tools/perf/util/parse-action.y | 13 ++++++++-
 4 files changed, 84 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
index 391546bf3d73..3b10cf9f99b3 100644
--- a/tools/perf/util/parse-action.c
+++ b/tools/perf/util/parse-action.c
@@ -7,6 +7,7 @@
  *
  * Supported expressions:
  *   - constant:
+ *     - integer
  */
 
 #include "util/debug.h"
@@ -118,7 +119,58 @@ void event_actions__free(void)
 	(void)event_actions__for_each_expr_safe(do_action_free, NULL, false);
 }
 
+static int expr_const_int_new(struct evtact_expr *expr, void *data, int size)
+{
+	if (data == NULL ||
+	    (size != sizeof(int)
+	     && size != sizeof(long) && size != sizeof(long long))) {
+		pr_err("expr_const_int size invalid: %d\n", size);
+		return -EINVAL;
+	}
+
+	expr->priv = malloc(sizeof(long long));
+	if (expr->priv == NULL) {
+		pr_err("exp_ const_int malloc failed\n");
+		return -ENOMEM;
+	}
+
+	if (size == sizeof(int))
+		*(unsigned long long *)(expr->priv) = *(unsigned int *)data;
+	else if (size == sizeof(long))
+		*(unsigned long long *)(expr->priv) = *(unsigned long *)data;
+	else if (size == sizeof(long long))
+		*(unsigned long long *)(expr->priv) = *(unsigned long long *)data;
+
+	INIT_LIST_HEAD(&expr->opnds);
+	return 0;
+}
+
+static void expr_const_int_free(struct evtact_expr *expr)
+{
+	zfree(&expr->priv);
+}
+
+static int expr_const_int_eval(struct evtact_expr *expr,
+			       void *in __maybe_unused, int in_size __maybe_unused,
+			       void **out, int *out_size)
+{
+	if (out != NULL)
+		*out = expr->priv;
+
+	if (out_size != NULL)
+		*out_size = sizeof(long long);
+
+	return 0;
+}
+
+static struct evtact_expr_ops expr_const_int_ops = {
+	.new  = expr_const_int_new,
+	.free = expr_const_int_free,
+	.eval = expr_const_int_eval,
+};
+
 static struct evtact_expr_ops *expr_const_ops_list[EVTACT_EXPR_CONST_TYPE_MAX] = {
+	[EVTACT_EXPR_CONST_TYPE_INT] = &expr_const_int_ops,
 };
 
 static int expr_const_set_ops(struct evtact_expr *expr, u32 opcode)
diff --git a/tools/perf/util/parse-action.h b/tools/perf/util/parse-action.h
index 47bd75264dee..ac81278c590e 100644
--- a/tools/perf/util/parse-action.h
+++ b/tools/perf/util/parse-action.h
@@ -14,6 +14,7 @@ enum evtact_expr_type {
 };
 
 enum evtact_expr_const_type {
+	EVTACT_EXPR_CONST_TYPE_INT,
 	EVTACT_EXPR_CONST_TYPE_MAX,
 };
 
diff --git a/tools/perf/util/parse-action.l b/tools/perf/util/parse-action.l
index 3cb72de50372..9237399a11ac 100644
--- a/tools/perf/util/parse-action.l
+++ b/tools/perf/util/parse-action.l
@@ -13,13 +13,32 @@
 #include "parse-action.h"
 #include "parse-action-bison.h"
 
+static int value(int base)
+{
+	unsigned long long num;
+
+	errno = 0;
+	num = strtoul(parse_action_text, NULL, base);
+	if (errno) {
+		pr_err("parse_action malloc number failed\n");
+		return ERROR;
+	}
+
+	parse_action_lval.num = num;
+	return NUMBER;
+}
+
 %}
 
+num_dec		[0-9]+
+num_hex		0[xX][0-9a-fA-F]+
 space		[ \t]
 ident		[_a-zA-Z][_a-zA-Z0-9]*
 
 %%
 
+{num_dec}	{ return value(10); }
+{num_hex}	{ return value(16); }
 {space}		{ }
 
 ";"		{ return SEMI; }
diff --git a/tools/perf/util/parse-action.y b/tools/perf/util/parse-action.y
index fade9d093d4a..51e77e54f157 100644
--- a/tools/perf/util/parse-action.y
+++ b/tools/perf/util/parse-action.y
@@ -17,6 +17,8 @@
 #include "util/debug.h"
 #include "util/parse-action.h"
 
+#define expr_id(t, o) evtact_expr_id_encode(EVTACT_EXPR_TYPE_##t, EVTACT_EXPR_##t##_TYPE_##o)
+
 int parse_action_lex(void);
 
 static void parse_action_error(struct list_head *expr __maybe_unused,
@@ -32,13 +34,15 @@ static void parse_action_error(struct list_head *expr __maybe_unused,
 	char *str;
 	struct evtact_expr *expr;
 	struct list_head *list;
+	unsigned long long num;
 }
 
-%token IDENT ERROR
+%token IDENT ERROR NUMBER
 %token SEMI
 %type <expr> action_term expr_term
 %destructor { parse_action_expr__free($$); } <expr>
 %type <str> IDENT
+%type <num> NUMBER
 
 %%
 
@@ -65,6 +69,13 @@ expr_term
 }
 
 expr_term:
+NUMBER
+{
+	$$ = parse_action_expr__new(expr_id(CONST, INT), NULL, (void *)&$1, sizeof($1));
+	if ($$ == NULL)
+		YYERROR;
+}
+|
 IDENT
 {
 	$$ = NULL;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [RFC 04/12] perf event action: Add parsing const string expr support
  2024-11-28 13:35 [RFC 00/12] perf record: Add event action support Yang Jihong
                   ` (2 preceding siblings ...)
  2024-11-28 13:35 ` [RFC 03/12] perf event action: Add parsing const integer " Yang Jihong
@ 2024-11-28 13:35 ` Yang Jihong
  2024-11-28 13:35 ` [RFC 05/12] perf event action: Add parsing call " Yang Jihong
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 27+ messages in thread
From: Yang Jihong @ 2024-11-28 13:35 UTC (permalink / raw)
  To: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
	jolsa, irogers, adrian.hunter, kan.liang, james.clark,
	linux-perf-users, linux-kernel
  Cc: yangjihong

Support parsing of constant string expression.

Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
---
 tools/perf/util/parse-action.c |  39 ++++++++++++
 tools/perf/util/parse-action.h |   1 +
 tools/perf/util/parse-action.l | 108 +++++++++++++++++++++++++++++++++
 tools/perf/util/parse-action.y |  10 ++-
 4 files changed, 157 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
index 3b10cf9f99b3..7e5ca889d5b9 100644
--- a/tools/perf/util/parse-action.c
+++ b/tools/perf/util/parse-action.c
@@ -8,6 +8,7 @@
  * Supported expressions:
  *   - constant:
  *     - integer
+ *     - string
  */
 
 #include "util/debug.h"
@@ -169,8 +170,46 @@ static struct evtact_expr_ops expr_const_int_ops = {
 	.eval = expr_const_int_eval,
 };
 
+static int expr_const_str_new(struct evtact_expr *expr,
+			      void *data, int size __maybe_unused)
+{
+	if (data == NULL) {
+		pr_err("exper const string is NULL\n");
+		return -EINVAL;
+	}
+
+	expr->priv = data;
+	INIT_LIST_HEAD(&expr->opnds);
+	return 0;
+}
+
+static void expr_const_str_free(struct evtact_expr *expr)
+{
+	zfree(&expr->priv);
+}
+
+static int expr_const_str_eval(struct evtact_expr *expr,
+			       void *in __maybe_unused, int in_size __maybe_unused,
+			       void **out, int *out_size)
+{
+	if (out != NULL)
+		*out = expr->priv;
+
+	if (out_size != NULL)
+		*out_size = strlen(expr->priv);
+
+	return 0;
+}
+
+static struct evtact_expr_ops expr_const_str_ops = {
+	.new  = expr_const_str_new,
+	.free = expr_const_str_free,
+	.eval = expr_const_str_eval,
+};
+
 static struct evtact_expr_ops *expr_const_ops_list[EVTACT_EXPR_CONST_TYPE_MAX] = {
 	[EVTACT_EXPR_CONST_TYPE_INT] = &expr_const_int_ops,
+	[EVTACT_EXPR_CONST_TYPE_STR] = &expr_const_str_ops,
 };
 
 static int expr_const_set_ops(struct evtact_expr *expr, u32 opcode)
diff --git a/tools/perf/util/parse-action.h b/tools/perf/util/parse-action.h
index ac81278c590e..ec422f8a05a7 100644
--- a/tools/perf/util/parse-action.h
+++ b/tools/perf/util/parse-action.h
@@ -15,6 +15,7 @@ enum evtact_expr_type {
 
 enum evtact_expr_const_type {
 	EVTACT_EXPR_CONST_TYPE_INT,
+	EVTACT_EXPR_CONST_TYPE_STR,
 	EVTACT_EXPR_CONST_TYPE_MAX,
 };
 
diff --git a/tools/perf/util/parse-action.l b/tools/perf/util/parse-action.l
index 9237399a11ac..f76240276b61 100644
--- a/tools/perf/util/parse-action.l
+++ b/tools/perf/util/parse-action.l
@@ -28,6 +28,54 @@ static int value(int base)
 	return NUMBER;
 }
 
+static char *str_buf;
+static unsigned int str_buf_size = 64;
+static int str_begin(void)
+{
+	if (str_buf == NULL) {
+		str_buf = malloc(str_buf_size);
+		if (str_buf == NULL) {
+			pr_err("parse_action malloc string buffer failed\n");
+			return ERROR;
+		}
+	}
+
+	str_buf[0] = '\0';
+	return 0;
+}
+
+static int str_write(const char *s)
+{
+	char *new_buf;
+
+	if (strlen(str_buf) + strlen(s) >= str_buf_size) {
+		str_buf_size = strlen(str_buf) + strlen(s) + 1;
+		new_buf = realloc(str_buf, str_buf_size);
+		if (new_buf == NULL) {
+			free(str_buf);
+			str_buf = NULL;
+			pr_err("parse_action realloc string buffer failed\n");
+			return ERROR;
+                } else {
+			str_buf = new_buf;
+		}
+	}
+
+	strcat(str_buf, s);
+	return 0;
+}
+
+static int str_end(void)
+{
+	parse_action_lval.str = strdup(str_buf);
+	if (parse_action_lval.str == NULL) {
+		pr_err("parse_action strdup string buffer failed\n");
+		return ERROR;
+	}
+
+	return STRING;
+}
+
 %}
 
 num_dec		[0-9]+
@@ -35,6 +83,8 @@ num_hex		0[xX][0-9a-fA-F]+
 space		[ \t]
 ident		[_a-zA-Z][_a-zA-Z0-9]*
 
+%x STR_BEGIN
+
 %%
 
 {num_dec}	{ return value(10); }
@@ -43,6 +93,64 @@ ident		[_a-zA-Z][_a-zA-Z0-9]*
 
 ";"		{ return SEMI; }
 
+\"		{
+			int ret;
+			yy_push_state(STR_BEGIN);
+			ret = str_begin();
+			if (ret) {
+				yy_pop_state();
+				return ret;
+			}
+		}
+<STR_BEGIN>{
+  \"		{
+			yy_pop_state();
+			return str_end();
+		}
+  [^\\\n\"]+	{
+			int ret;
+			ret = str_write((const char *)yytext);
+			if (ret) {
+				yy_pop_state();
+				return ret;
+			}
+		}
+  \\n		{
+			int ret;
+			ret = str_write("\n");
+			if (ret) {
+				yy_pop_state();
+				return ret;
+                        }
+                }
+  \\t           {
+			int ret;
+			ret = str_write("\t");
+			if (ret) {
+				yy_pop_state();
+				return ret;
+			}
+		}
+  \\\\		{
+			int ret;
+			ret = str_write("\\");
+			if (ret) {
+				yy_pop_state();
+				return ret;
+			}
+		}
+  \\.		{
+			yy_pop_state();
+			pr_err("parse_action invalid escape character: '%s'\n", parse_action_text);
+			return ERROR;
+		}
+  .		{
+			yy_pop_state();
+			pr_err("parse_action invalid character: '%s'\n", parse_action_text);
+			return ERROR;
+		}
+}
+
 {ident}		{
 			parse_action_lval.str = strdup(parse_action_text);
 			if (parse_action_lval.str == NULL) {
diff --git a/tools/perf/util/parse-action.y b/tools/perf/util/parse-action.y
index 51e77e54f157..4922b2d94aee 100644
--- a/tools/perf/util/parse-action.y
+++ b/tools/perf/util/parse-action.y
@@ -37,12 +37,13 @@ static void parse_action_error(struct list_head *expr __maybe_unused,
 	unsigned long long num;
 }
 
-%token IDENT ERROR NUMBER
+%token IDENT ERROR NUMBER STRING
 %token SEMI
 %type <expr> action_term expr_term
 %destructor { parse_action_expr__free($$); } <expr>
 %type <str> IDENT
 %type <num> NUMBER
+%type <str> STRING
 
 %%
 
@@ -76,6 +77,13 @@ NUMBER
 		YYERROR;
 }
 |
+STRING
+{
+	$$ = parse_action_expr__new(expr_id(CONST, STR), NULL, (void *)$1, strlen($1));
+	if ($$ == NULL)
+		YYERROR;
+}
+|
 IDENT
 {
 	$$ = NULL;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [RFC 05/12] perf event action: Add parsing call expr support
  2024-11-28 13:35 [RFC 00/12] perf record: Add event action support Yang Jihong
                   ` (3 preceding siblings ...)
  2024-11-28 13:35 ` [RFC 04/12] perf event action: Add parsing const string " Yang Jihong
@ 2024-11-28 13:35 ` Yang Jihong
  2024-11-28 13:35 ` [RFC 06/12] perf event action: Add parsing print() " Yang Jihong
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 27+ messages in thread
From: Yang Jihong @ 2024-11-28 13:35 UTC (permalink / raw)
  To: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
	jolsa, irogers, adrian.hunter, kan.liang, james.clark,
	linux-perf-users, linux-kernel
  Cc: yangjihong

Add support for parsing function call expression, the format is similar to
C language function call, with parameters specified after function name,
inside the parentheses and separated with a comma.

Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
---
 tools/perf/util/parse-action.c | 25 +++++++++++++++
 tools/perf/util/parse-action.h |  5 +++
 tools/perf/util/parse-action.l |  3 ++
 tools/perf/util/parse-action.y | 56 +++++++++++++++++++++++++++++++---
 4 files changed, 85 insertions(+), 4 deletions(-)

diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
index 7e5ca889d5b9..40e7c8aad7be 100644
--- a/tools/perf/util/parse-action.c
+++ b/tools/perf/util/parse-action.c
@@ -9,6 +9,7 @@
  *   - constant:
  *     - integer
  *     - string
+ *   - call:
  */
 
 #include "util/debug.h"
@@ -232,8 +233,32 @@ static struct evtact_expr_class expr_const = {
 	.set_ops = expr_const_set_ops,
 };
 
+static struct evtact_expr_ops *expr_call_ops_list[EVTACT_EXPR_CALL_TYPE_MAX] = {
+};
+
+static int expr_call_set_ops(struct evtact_expr *expr, u32 opcode)
+{
+	if (opcode >= EVTACT_EXPR_CALL_TYPE_MAX) {
+		pr_err("expr_call opcode invalid: %u\n", opcode);
+		return -EINVAL;
+	}
+
+	if (expr_call_ops_list[opcode] == NULL) {
+		pr_err("expr_call opcode not supported: %u\n", opcode);
+		return -ENOTSUP;
+	}
+
+	expr->ops = expr_call_ops_list[opcode];
+	return 0;
+}
+
+static struct evtact_expr_class expr_call = {
+	.set_ops = expr_call_set_ops,
+};
+
 static struct evtact_expr_class *expr_class_list[EVTACT_EXPR_TYPE_MAX] = {
 	[EVTACT_EXPR_TYPE_CONST]   = &expr_const,
+	[EVTACT_EXPR_TYPE_CALL]    = &expr_call,
 };
 
 int parse_action_expr__set_class(enum evtact_expr_type type,
diff --git a/tools/perf/util/parse-action.h b/tools/perf/util/parse-action.h
index ec422f8a05a7..30c2fd6e81d0 100644
--- a/tools/perf/util/parse-action.h
+++ b/tools/perf/util/parse-action.h
@@ -10,6 +10,7 @@
 
 enum evtact_expr_type {
 	EVTACT_EXPR_TYPE_CONST,
+	EVTACT_EXPR_TYPE_CALL,
 	EVTACT_EXPR_TYPE_MAX,
 };
 
@@ -19,6 +20,10 @@ enum evtact_expr_const_type {
 	EVTACT_EXPR_CONST_TYPE_MAX,
 };
 
+enum evtact_expr_call_type {
+	EVTACT_EXPR_CALL_TYPE_MAX,
+};
+
 struct evtact_expr;
 struct evtact_expr_ops {
 	int (*new)(struct evtact_expr *expr, void *data, int size);
diff --git a/tools/perf/util/parse-action.l b/tools/perf/util/parse-action.l
index f76240276b61..189f73dfc3b1 100644
--- a/tools/perf/util/parse-action.l
+++ b/tools/perf/util/parse-action.l
@@ -92,6 +92,9 @@ ident		[_a-zA-Z][_a-zA-Z0-9]*
 {space}		{ }
 
 ";"		{ return SEMI; }
+"("		{ return LP;   }
+")"		{ return RP;   }
+","		{ return COM;  }
 
 \"		{
 			int ret;
diff --git a/tools/perf/util/parse-action.y b/tools/perf/util/parse-action.y
index 4922b2d94aee..1b162c694218 100644
--- a/tools/perf/util/parse-action.y
+++ b/tools/perf/util/parse-action.y
@@ -35,15 +35,18 @@ static void parse_action_error(struct list_head *expr __maybe_unused,
 	struct evtact_expr *expr;
 	struct list_head *list;
 	unsigned long long num;
+	u32 opcode;
 }
 
-%token IDENT ERROR NUMBER STRING
-%token SEMI
-%type <expr> action_term expr_term
+%token IDENT ERROR NUMBER STRING CALL
+%token SEMI LP RP COM
+%type <expr> action_term expr_term expr_call_term
 %destructor { parse_action_expr__free($$); } <expr>
 %type <str> IDENT
 %type <num> NUMBER
 %type <str> STRING
+%type <opcode> CALL
+%type <list> opnds
 
 %%
 
@@ -64,11 +67,56 @@ action_term
 }
 
 action_term:
-expr_term
+expr_call_term
 {
 	$$ = $1;
 }
 
+expr_call_term:
+CALL LP RP
+{
+	$$ = parse_action_expr__new(evtact_expr_id_encode(EVTACT_EXPR_TYPE_CALL, $1), NULL, NULL, 0);
+	if ($$ == NULL)
+		YYERROR;
+}
+|
+CALL LP opnds RP
+{
+	$$ = parse_action_expr__new(evtact_expr_id_encode(EVTACT_EXPR_TYPE_CALL, $1), $3, NULL, 0);
+	if ($$ == NULL)
+		YYERROR;
+}
+|
+IDENT LP RP
+{
+	$$ = NULL;
+	pr_err("unknown function '%s()'\n", $1);
+	free($1);
+	YYERROR;
+}
+|
+IDENT LP opnds RP
+{
+	$$ = NULL;
+	pr_err("unknown function '%s()'\n", $1);
+	parse_action_expr__free_opnds($3);
+	free($1);
+	YYERROR;
+}
+
+opnds:
+opnds COM expr_term
+{
+	list_add_tail(&$3->list, $1);
+	$$ = $1;
+}
+|
+expr_term
+{
+	INIT_LIST_HEAD(&$1->list);
+	$$ = &$1->list;
+}
+
 expr_term:
 NUMBER
 {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [RFC 06/12] perf event action: Add parsing print() call expr support
  2024-11-28 13:35 [RFC 00/12] perf record: Add event action support Yang Jihong
                   ` (4 preceding siblings ...)
  2024-11-28 13:35 ` [RFC 05/12] perf event action: Add parsing call " Yang Jihong
@ 2024-11-28 13:35 ` Yang Jihong
  2024-11-28 13:35 ` [RFC 07/12] perf event action: Add parsing builtin " Yang Jihong
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 27+ messages in thread
From: Yang Jihong @ 2024-11-28 13:35 UTC (permalink / raw)
  To: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
	jolsa, irogers, adrian.hunter, kan.liang, james.clark,
	linux-perf-users, linux-kernel
  Cc: yangjihong

Add support for parsing print() function calls, the parameter format is
similar to C's printf(), the first parameter is a string format,
and format specifiers support %c, %d, %d, %o, %u, %x, %X, %s.
Currently, it simply checks whether the number of arguments matches
the number of format specifiers.

Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
---
 tools/perf/util/parse-action.c | 329 +++++++++++++++++++++++++++++++++
 tools/perf/util/parse-action.h |   1 +
 tools/perf/util/parse-action.l |   9 +
 3 files changed, 339 insertions(+)

diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
index 40e7c8aad7be..95c06cc071ad 100644
--- a/tools/perf/util/parse-action.c
+++ b/tools/perf/util/parse-action.c
@@ -10,8 +10,11 @@
  *     - integer
  *     - string
  *   - call:
+ *     - print
  */
 
+#include <regex.h>
+
 #include "util/debug.h"
 #include "util/parse-action.h"
 #include "util/parse-action-flex.h"
@@ -171,6 +174,12 @@ static struct evtact_expr_ops expr_const_int_ops = {
 	.eval = expr_const_int_eval,
 };
 
+static bool is_const_str_expr(struct evtact_expr *expr)
+{
+	return expr->id == evtact_expr_id_encode(EVTACT_EXPR_TYPE_CONST,
+						 EVTACT_EXPR_CONST_TYPE_STR);
+}
+
 static int expr_const_str_new(struct evtact_expr *expr,
 			      void *data, int size __maybe_unused)
 {
@@ -233,7 +242,327 @@ static struct evtact_expr_class expr_const = {
 	.set_ops = expr_const_set_ops,
 };
 
+enum print_fmt_spec_len {
+	PRINT_FMT_SPEC_LEN_INT,
+	PRINT_FMT_SPEC_LEN_LONG,
+	PRINT_FMT_SPEC_LEN_LONG_LONG,
+};
+
+enum print_fmt_spec_type {
+	PRINT_FMT_SPEC_TYPE_VOID,
+	PRINT_FMT_SPEC_TYPE_INT,
+	PRINT_FMT_SPEC_TYPE_STRING,
+};
+
+struct print_fmt_spec {
+	struct list_head list;
+	enum print_fmt_spec_type type;
+	enum print_fmt_spec_len len;
+	char *str;
+};
+
+#define PRINT_OUT_BUF_DEFAULT_LEN 64
+struct print_expr_priv {
+	struct list_head fmt_specs;
+	int fmt_spec_num;
+	int args_num;
+	char *out;
+	int out_len;
+};
+
+static int print_fmt_add_spec(struct print_expr_priv *priv, char *s,
+			      regoff_t len, enum print_fmt_spec_len spec_len,
+			      enum print_fmt_spec_type spec_type)
+{
+	struct print_fmt_spec *spec;
+
+	spec = malloc(sizeof(*spec));
+	if (spec == NULL) {
+		pr_err("call print fmt spec malloc failed\n");
+		return -ENOMEM;
+	}
+
+	spec->str = strndup(s, len);
+	if (spec->str == NULL) {
+		pr_err("call print fmt spec strndup failed\n");
+		free(spec);
+		return -ENOMEM;
+	}
+
+	spec->len = spec_len;
+	spec->type = spec_type;
+	list_add_tail(&spec->list, &priv->fmt_specs);
+	return 0;
+}
+
+static int print_fmt_spec_get_len(char *s, regoff_t len)
+{
+	if (len == 1 && !strncmp(s, "l", len))
+		return PRINT_FMT_SPEC_LEN_LONG;
+	else if (len == 2 && !strncmp(s, "ll", len))
+		return PRINT_FMT_SPEC_LEN_LONG_LONG;
+
+	return PRINT_FMT_SPEC_LEN_INT;
+}
+
+static int print_fmt_spec_get_type(char *s)
+{
+	switch (s[0]) {
+	case 'c':
+	case 'd':
+	case 'o':
+	case 'u':
+	case 'x':
+	case 'X':
+		return PRINT_FMT_SPEC_TYPE_INT;
+	case 's':
+		return PRINT_FMT_SPEC_TYPE_STRING;
+	default:
+		break;
+	}
+
+	return PRINT_FMT_SPEC_TYPE_VOID;
+}
+
+static int print_fmt_get_string(struct evtact_expr *expr, char **fmt)
+{
+	int ret, fmt_len;
+	struct evtact_expr *fmt_expr;
+
+	fmt_expr = list_first_entry_or_null(&expr->opnds, struct evtact_expr, list);
+	if (fmt_expr == NULL) {
+		pr_err("print() requires at least one argument\n");
+		return -EINVAL;
+	} else if (!is_const_str_expr(fmt_expr)) {
+		pr_err("print() first argument expected to be string\n");
+		return -EINVAL;
+	}
+
+	if (fmt_expr->ops->eval != NULL) {
+		ret = fmt_expr->ops->eval(fmt_expr, NULL, 0, (void **)fmt, &fmt_len);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static void print_fmt_free_specs(struct print_expr_priv *priv)
+{
+	struct print_fmt_spec *spec, *tmp;
+
+	if (priv == NULL)
+		return;
+
+	list_for_each_entry_safe(spec, tmp, &priv->fmt_specs, list) {
+		list_del(&spec->list);
+		free(spec->str);
+		free(spec);
+	}
+}
+
+static int print_fmt_split(struct evtact_expr *expr, struct print_expr_priv *priv)
+{
+	int i, ret;
+	char *s, *fmt;
+	regex_t regex;
+	regmatch_t pmatch[6];
+	int spec_len, spec_type;
+	const char *const re = "%(-?)([0-9]*)(\\.[0-9]+)?(ll|l)?([cdosuxX])";
+
+	ret = print_fmt_get_string(expr, &fmt);
+	if (ret)
+		return ret;
+
+	if (regcomp(&regex, re, REG_EXTENDED)) {
+		pr_err("expr call print fmt regcomp failed\n");
+		return -1;
+	}
+
+	s = fmt;
+	for (i = 0;; i++) {
+		if (regexec(&regex, s, ARRAY_SIZE(pmatch), pmatch, 0))
+			break;
+
+		spec_len = print_fmt_spec_get_len(s + pmatch[4].rm_so,
+						  pmatch[4].rm_eo - pmatch[4].rm_so);
+		spec_type = print_fmt_spec_get_type(s + pmatch[5].rm_so);
+
+		ret = print_fmt_add_spec(priv, s, pmatch[0].rm_eo, spec_len, spec_type);
+		if (ret)
+			goto out_free_specs;
+
+		s += pmatch[0].rm_eo;
+	}
+
+	if ((size_t)(s - fmt) < strlen(fmt)) {
+		ret = print_fmt_add_spec(priv, s, strlen(fmt) - (s - fmt),
+					 PRINT_FMT_SPEC_LEN_INT, PRINT_FMT_SPEC_TYPE_VOID);
+		if (ret)
+			goto out_free_specs;
+	}
+
+	priv->fmt_spec_num = i;
+	return 0;
+
+out_free_specs:
+	print_fmt_free_specs(priv);
+	return ret;
+}
+
+static int print_check_args(struct evtact_expr *expr, struct print_expr_priv *priv)
+{
+	struct evtact_expr *arg;
+
+	priv->args_num = 0;
+	list_for_each_entry(arg, &expr->opnds, list)
+		priv->args_num++;
+
+	/* do not count format string argument */
+	priv->args_num--;
+
+	if (priv->args_num != priv->fmt_spec_num) {
+		pr_err("print() arguments number for format string mismatch: %d expected, %d provided\n",
+		       priv->fmt_spec_num, priv->args_num);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int expr_call_print_new(struct evtact_expr *expr,
+			       void *data __maybe_unused, int size __maybe_unused)
+{
+	int ret;
+	struct print_expr_priv *priv;
+
+	priv = malloc(sizeof(struct print_expr_priv));
+	if (priv == NULL)
+		return -ENOMEM;
+
+	priv->out = malloc(PRINT_OUT_BUF_DEFAULT_LEN);
+	if (priv->out == NULL) {
+		ret = -ENOMEM;
+		goto out_free_priv;
+	}
+	priv->out_len = PRINT_OUT_BUF_DEFAULT_LEN;
+
+	INIT_LIST_HEAD(&priv->fmt_specs);
+	ret = print_fmt_split(expr, priv);
+	if (ret)
+		goto out_free_out_buf;
+
+	ret = print_check_args(expr, priv);
+	if (ret)
+		goto out_free_specs;
+
+	expr->priv = priv;
+	return 0;
+
+out_free_specs:
+	print_fmt_free_specs(priv);
+out_free_out_buf:
+	free(priv->out);
+	priv->out_len = 0;
+out_free_priv:
+	free(priv);
+	return ret;
+}
+
+static void expr_call_print_free(struct evtact_expr *expr)
+{
+	struct print_expr_priv *priv;
+
+	priv = expr->priv;
+	if (priv == NULL)
+		return;
+
+	print_fmt_free_specs(expr->priv);
+	zfree(&priv->out);
+	priv->out_len = 0;
+	zfree(&expr->priv);
+}
+
+static int expr_call_print_eval(struct evtact_expr *expr,
+				void *in, int in_size,
+				void **out __maybe_unused, int *out_size __maybe_unused)
+{
+	int ret, len;
+	char *buf_out;
+	int arg_val_size;
+	unsigned long long *arg_val;
+	struct evtact_expr *arg;
+	struct print_fmt_spec *spec;
+	struct print_expr_priv *priv = expr->priv;
+
+retry:
+	len = 0;
+	priv->out[0] = '\0';
+	arg = list_first_entry(&expr->opnds, struct evtact_expr, list);
+	list_for_each_entry(spec, &priv->fmt_specs, list) {
+		if (spec->type == PRINT_FMT_SPEC_TYPE_VOID) {
+			len += snprintf(priv->out + len, priv->out_len - len, "%s", spec->str);
+		} else {
+			arg = list_next_entry(arg, list);
+			if (arg == NULL) {
+				pr_err("expr call print arguments are empty\n");
+				return -EINVAL;
+			}
+
+			ret = arg->ops->eval(arg, in, in_size, (void **)&arg_val, &arg_val_size);
+			if (ret) {
+				pr_err("expr call print eval argument failed %d\n", ret);
+				return ret;
+			}
+
+			if (spec->type == PRINT_FMT_SPEC_TYPE_STRING) {
+				len += snprintf(priv->out + len, priv->out_len - len,
+						spec->str, arg_val);
+			} else if (spec->type == PRINT_FMT_SPEC_TYPE_INT) {
+				switch (spec->len) {
+				case PRINT_FMT_SPEC_LEN_INT:
+					len += snprintf(priv->out + len, priv->out_len - len,
+							spec->str, *(unsigned int *)arg_val);
+					break;
+				case PRINT_FMT_SPEC_LEN_LONG:
+					len += snprintf(priv->out + len, priv->out_len - len,
+							spec->str, *(unsigned long *)arg_val);
+					break;
+				case PRINT_FMT_SPEC_LEN_LONG_LONG:
+					len += snprintf(priv->out + len, priv->out_len - len,
+							spec->str, *(unsigned long long *)arg_val);
+					break;
+				default:
+					break;
+				}
+			}
+		}
+
+		if (len >= priv->out_len) {
+			buf_out = realloc(priv->out, priv->out_len << 1);
+			if (buf_out != NULL) {
+				priv->out = buf_out;
+				priv->out_len <<= 1;
+				goto retry;
+			}
+		}
+
+		if (len >= priv->out_len)
+			break;
+	}
+
+	printf("%s", priv->out);
+	return 0;
+}
+
+static struct evtact_expr_ops expr_call_print_ops = {
+	.new  = expr_call_print_new,
+	.free = expr_call_print_free,
+	.eval = expr_call_print_eval,
+};
+
 static struct evtact_expr_ops *expr_call_ops_list[EVTACT_EXPR_CALL_TYPE_MAX] = {
+	[EVTACT_EXPR_CALL_TYPE_PRINT] = &expr_call_print_ops,
 };
 
 static int expr_call_set_ops(struct evtact_expr *expr, u32 opcode)
diff --git a/tools/perf/util/parse-action.h b/tools/perf/util/parse-action.h
index 30c2fd6e81d0..f8aece601a84 100644
--- a/tools/perf/util/parse-action.h
+++ b/tools/perf/util/parse-action.h
@@ -21,6 +21,7 @@ enum evtact_expr_const_type {
 };
 
 enum evtact_expr_call_type {
+	EVTACT_EXPR_CALL_TYPE_PRINT,
 	EVTACT_EXPR_CALL_TYPE_MAX,
 };
 
diff --git a/tools/perf/util/parse-action.l b/tools/perf/util/parse-action.l
index 189f73dfc3b1..5e75383ba3e7 100644
--- a/tools/perf/util/parse-action.l
+++ b/tools/perf/util/parse-action.l
@@ -76,6 +76,12 @@ static int str_end(void)
 	return STRING;
 }
 
+static int call(u32 opcode)
+{
+	parse_action_lval.opcode = opcode;
+	return CALL;
+}
+
 %}
 
 num_dec		[0-9]+
@@ -91,6 +97,9 @@ ident		[_a-zA-Z][_a-zA-Z0-9]*
 {num_hex}	{ return value(16); }
 {space}		{ }
 
+
+print		{ return call(EVTACT_EXPR_CALL_TYPE_PRINT); }
+
 ";"		{ return SEMI; }
 "("		{ return LP;   }
 ")"		{ return RP;   }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [RFC 07/12] perf event action: Add parsing builtin expr support
  2024-11-28 13:35 [RFC 00/12] perf record: Add event action support Yang Jihong
                   ` (5 preceding siblings ...)
  2024-11-28 13:35 ` [RFC 06/12] perf event action: Add parsing print() " Yang Jihong
@ 2024-11-28 13:35 ` Yang Jihong
  2024-11-28 13:35 ` [RFC 08/12] perf event action: Add parsing builtin cpu " Yang Jihong
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 27+ messages in thread
From: Yang Jihong @ 2024-11-28 13:35 UTC (permalink / raw)
  To: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
	jolsa, irogers, adrian.hunter, kan.liang, james.clark,
	linux-perf-users, linux-kernel
  Cc: yangjihong

builtin exprs are some built-in special variables (such as cpu, tid, pid).
The bpf program attaches to corresponding event, saves sample data to bpf
perf_event ringuffer in handler, perf-tool read data and run actions.

Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
---
 tools/perf/Makefile.perf                     |   1 +
 tools/perf/util/bpf_skel/bpf_record_action.h |  19 +
 tools/perf/util/bpf_skel/record_action.bpf.c |  74 ++++
 tools/perf/util/parse-action.c               |  22 ++
 tools/perf/util/parse-action.h               |   5 +
 tools/perf/util/parse-action.l               |   6 +
 tools/perf/util/parse-action.y               |  11 +-
 tools/perf/util/record_action.c              | 352 ++++++++++++++++++-
 tools/perf/util/record_action.h              |   8 +-
 9 files changed, 492 insertions(+), 6 deletions(-)
 create mode 100644 tools/perf/util/bpf_skel/bpf_record_action.h
 create mode 100644 tools/perf/util/bpf_skel/record_action.bpf.c

diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index d74241a15131..07bc2f8e2565 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -1173,6 +1173,7 @@ SKELETONS += $(SKEL_OUT)/kwork_trace.skel.h $(SKEL_OUT)/sample_filter.skel.h
 SKELETONS += $(SKEL_OUT)/kwork_top.skel.h
 SKELETONS += $(SKEL_OUT)/bench_uprobe.skel.h
 SKELETONS += $(SKEL_OUT)/augmented_raw_syscalls.skel.h
+SKELETONS += $(SKEL_OUT)/record_action.skel.h
 
 $(SKEL_TMP_OUT) $(LIBAPI_OUTPUT) $(LIBBPF_OUTPUT) $(LIBPERF_OUTPUT) $(LIBSUBCMD_OUTPUT) $(LIBSYMBOL_OUTPUT):
 	$(Q)$(MKDIR) -p $@
diff --git a/tools/perf/util/bpf_skel/bpf_record_action.h b/tools/perf/util/bpf_skel/bpf_record_action.h
new file mode 100644
index 000000000000..ee4d03848e58
--- /dev/null
+++ b/tools/perf/util/bpf_skel/bpf_record_action.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __PERF_UTIL_BPF_SKEL_BPF_RECORD_ACTION_H_
+#define __PERF_UTIL_BPF_SKEL_BPF_RECORD_ACTION_H_
+
+#define __TASK_COMM_MAX_SIZE 16
+
+#define __OUTPUT_FORMATS_MAX_NUM 8
+
+enum __output_format_type {
+	__OUTPUT_FORMAT_TYPE_MAX,
+};
+
+#define __OUTPUT_DATA_MAX_SIZE 256
+struct __output_data_payload {
+	__u32 __size;
+	__u8 __data[__OUTPUT_DATA_MAX_SIZE];
+};
+
+#endif /* __PERF_UTIL_BPF_SKEL_BPF_RECORD_ACTION_H_ */
diff --git a/tools/perf/util/bpf_skel/record_action.bpf.c b/tools/perf/util/bpf_skel/record_action.bpf.c
new file mode 100644
index 000000000000..424fa8c3e6f1
--- /dev/null
+++ b/tools/perf/util/bpf_skel/record_action.bpf.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+
+#include "bpf_record_action.h"
+
+int output_formats[__OUTPUT_FORMATS_MAX_NUM] = { 0 };
+int output_format_num = 0;
+int enabled = 0;
+
+#define MAX_CPUS  1024
+
+/* bpf-output associated map */
+struct __sample_data__ {
+	__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
+	__type(key, int);
+	__type(value, __u32);
+	__uint(max_entries, MAX_CPUS);
+} __sample_data__ SEC(".maps");
+
+struct sample_data_tmp {
+	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+	__type(key, int);
+	__type(value, struct __output_data_payload);
+	__uint(max_entries, 1);
+} sample_data_tmp SEC(".maps");
+
+static inline struct __output_data_payload *sample_data_payload(void)
+{
+	int key = 0;
+
+	return bpf_map_lookup_elem(&sample_data_tmp, &key);
+}
+
+SEC("xxx")
+int sample_output(u64 *ctx)
+{
+	struct __output_data_payload *sample;
+	int i;
+	int total = 0;
+	int ret = 0;
+
+	if (!enabled)
+		return 0;
+
+	sample = sample_data_payload();
+	if (!sample)
+		return 0;
+
+	for (i = 0; i < output_format_num && i < __OUTPUT_FORMATS_MAX_NUM; i++) {
+		switch (output_formats[i]) {
+		default:
+			ret = -1;
+			break;
+		}
+
+		if (ret < 0)
+			return 0;
+
+		total += ret;
+		if (total > __OUTPUT_DATA_MAX_SIZE)
+			return 0;
+	}
+
+	sample->__size = total;
+	bpf_perf_event_output(ctx, &__sample_data__, BPF_F_CURRENT_CPU,
+			      sample, sizeof(__u32) + total);
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
index 95c06cc071ad..e6299de99bc5 100644
--- a/tools/perf/util/parse-action.c
+++ b/tools/perf/util/parse-action.c
@@ -11,6 +11,7 @@
  *     - string
  *   - call:
  *     - print
+ *   - builtin:
  */
 
 #include <regex.h>
@@ -19,6 +20,7 @@
 #include "util/parse-action.h"
 #include "util/parse-action-flex.h"
 #include "util/parse-action-bison.h"
+#include "util/record_action.h"
 
 static struct list_head actions_head = LIST_HEAD_INIT(actions_head);
 
@@ -90,10 +92,30 @@ static int parse_action_option(const char *str)
 	return ret;
 }
 
+static bool initialized = false;
+static int parse_action_init(void)
+{
+	int ret;
+
+	if (initialized)
+		return 0;
+
+	ret = bpf_perf_record_init();
+	if (ret)
+		return ret;
+
+	initialized = true;
+	return 0;
+}
+
 int parse_record_action(struct evlist *evlist, const char *str)
 {
 	int ret;
 
+	ret = parse_action_init();
+	if (ret)
+		return ret;
+
 	if (evlist == NULL) {
 		pr_err("--action option should follow a tracer option\n");
 		return -1;
diff --git a/tools/perf/util/parse-action.h b/tools/perf/util/parse-action.h
index f8aece601a84..b1a33a77c558 100644
--- a/tools/perf/util/parse-action.h
+++ b/tools/perf/util/parse-action.h
@@ -11,6 +11,7 @@
 enum evtact_expr_type {
 	EVTACT_EXPR_TYPE_CONST,
 	EVTACT_EXPR_TYPE_CALL,
+	EVTACT_EXPR_TYPE_BUILTIN,
 	EVTACT_EXPR_TYPE_MAX,
 };
 
@@ -25,6 +26,10 @@ enum evtact_expr_call_type {
 	EVTACT_EXPR_CALL_TYPE_MAX,
 };
 
+enum evtact_expr_builtin_type {
+	EVTACT_EXPR_BUILTIN_TYPE_MAX,
+};
+
 struct evtact_expr;
 struct evtact_expr_ops {
 	int (*new)(struct evtact_expr *expr, void *data, int size);
diff --git a/tools/perf/util/parse-action.l b/tools/perf/util/parse-action.l
index 5e75383ba3e7..1c729b7a0248 100644
--- a/tools/perf/util/parse-action.l
+++ b/tools/perf/util/parse-action.l
@@ -82,6 +82,12 @@ static int call(u32 opcode)
 	return CALL;
 }
 
+static int builtin(u32 opcode)
+{
+	parse_action_lval.opcode = opcode;
+	return BUILTIN;
+}
+
 %}
 
 num_dec		[0-9]+
diff --git a/tools/perf/util/parse-action.y b/tools/perf/util/parse-action.y
index 1b162c694218..78a15f146ade 100644
--- a/tools/perf/util/parse-action.y
+++ b/tools/perf/util/parse-action.y
@@ -38,14 +38,14 @@ static void parse_action_error(struct list_head *expr __maybe_unused,
 	u32 opcode;
 }
 
-%token IDENT ERROR NUMBER STRING CALL
+%token IDENT ERROR NUMBER STRING CALL BUILTIN
 %token SEMI LP RP COM
 %type <expr> action_term expr_term expr_call_term
 %destructor { parse_action_expr__free($$); } <expr>
 %type <str> IDENT
 %type <num> NUMBER
 %type <str> STRING
-%type <opcode> CALL
+%type <opcode> CALL BUILTIN
 %type <list> opnds
 
 %%
@@ -132,6 +132,13 @@ STRING
 		YYERROR;
 }
 |
+BUILTIN
+{
+	$$ = parse_action_expr__new(evtact_expr_id_encode(EVTACT_EXPR_TYPE_BUILTIN, $1), NULL, NULL, 0);
+	if ($$ == NULL)
+		YYERROR;
+}
+|
 IDENT
 {
 	$$ = NULL;
diff --git a/tools/perf/util/record_action.c b/tools/perf/util/record_action.c
index 44789e0d4678..634d6b199bae 100644
--- a/tools/perf/util/record_action.c
+++ b/tools/perf/util/record_action.c
@@ -1,15 +1,361 @@
 // SPDX-License-Identifier: GPL-2.0
 /**
- * Read event sample data and execute the specified actions.
+ * Read perf event output sample data and execute the specified actions.
  */
 
+#include <errno.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <linux/err.h>
+
 #include "util/debug.h"
+#include "util/target.h"
 #include "util/parse-action.h"
 #include "util/record_action.h"
 
-int bpf_perf_record(struct evlist *evlist __maybe_unused,
-		    int argc __maybe_unused, const char **argv __maybe_unused)
+#include "util/bpf_counter.h"
+#include "util/bpf_skel/bpf_record_action.h"
+#include "util/bpf_skel/record_action.skel.h"
+
+static struct perf_buffer *pb;
+static struct record_action_bpf *skel;
+
+struct expr_builtin_output_priv {
+	int offset;
+	int size;
+};
+
+static int bpf_expr_builtin_new(struct evtact_expr *expr,
+				void *data __maybe_unused, int size __maybe_unused)
+{
+	struct expr_builtin_output_priv *priv;
+
+	priv = malloc(sizeof(*priv));
+	if (priv == NULL) {
+		pr_err("bpf expr builtin priv malloc failed\n");
+		return -ENOMEM;
+	}
+
+	expr->priv = priv;
+	return 0;
+}
+
+static void bpf_expr_builtin_free(struct evtact_expr *expr)
+{
+	zfree(&expr->priv);
+}
+
+static int bpf_expr_builtin_eval(struct evtact_expr *expr,
+				 void *in, int in_size, void **out, int *out_size)
+{
+	struct expr_builtin_output_priv *priv = expr->priv;
+
+	if (in_size < priv->size)
+		return -EINVAL;
+
+	*out = (u8 *)in + priv->offset;
+	*out_size = priv->size;
+	return 0;
+}
+
+static struct evtact_expr_ops bpf_expr_builtin_common = {
+	.new  = bpf_expr_builtin_new,
+	.free = bpf_expr_builtin_free,
+	.eval = bpf_expr_builtin_eval,
+};
+
+static int bpf_expr_builtin_set_ops(struct evtact_expr *expr, u32 opcode)
+{
+	if (opcode >= EVTACT_EXPR_BUILTIN_TYPE_MAX) {
+		pr_err("bpf expr_builtin opcode invalid: %u\n", opcode);
+		return -EINVAL;
+	}
+
+	expr->ops = &bpf_expr_builtin_common;
+	return 0;
+}
+
+static struct evtact_expr_class bpf_expr_builtin = {
+	.set_ops = bpf_expr_builtin_set_ops,
+};
+
+int bpf_perf_record_init(void)
+{
+	return parse_action_expr__set_class(EVTACT_EXPR_TYPE_BUILTIN,
+					    &bpf_expr_builtin);
+}
+
+static int set_expr_builtin_output_format(struct evtact_expr *expr,
+					  u32 opcode, int *offset,
+					  int *format __maybe_unused)
+{
+	int size = 0;
+	struct expr_builtin_output_priv *priv = expr->priv;
+
+	switch (opcode) {
+	default:
+		pr_err("set expr builtin output format unknown opcode: %u\n", opcode);
+		return -1;
+	}
+
+	priv->offset = *offset;
+	priv->size = size;
+	*offset += size;
+	return 0;
+}
+
+struct output_args {
+	int *num;
+	int *offset;
+	int *formats;
+};
+
+static int do_set_output_format(struct evtact_expr *expr, void *data)
+{
+	int ret;
+	u32 type, opcode;
+	struct output_args *args = data;
+	int num = *(args->num);
+
+	evtact_expr_id_decode(expr->id, &type, &opcode);
+	if (type == EVTACT_EXPR_TYPE_BUILTIN) {
+		if (num >= __OUTPUT_FORMATS_MAX_NUM) {
+			pr_err("bpf record action output formats too many\n");
+			return -1;
+		}
+
+		ret = set_expr_builtin_output_format(expr, opcode, args->offset,
+						     args->formats + num);
+		if (ret)
+			return ret;
+		num++;
+	}
+
+	*(args->num) = num;
+	return 0;
+}
+
+static int bpf_set_output_format(int *formats)
+{
+	int ret;
+	int offset = 0;
+	int num = 0;
+	struct output_args args = {
+		.num     = &num,
+		.offset  = &offset,
+		.formats = formats,
+	};
+
+	ret = event_actions__for_each_expr(do_set_output_format, &args, true);
+	if (ret)
+		return ret;
+
+	if (offset > __OUTPUT_DATA_MAX_SIZE) {
+		pr_err("bpf record action output too large\n");
+		return -1;
+	}
+
+	skel->bss->output_format_num = num;
+	return 0;
+}
+
+
+struct eval_args {
+	void *data;
+	__u32 size;
+};
+
+static int do_sample_handler(struct evtact_expr *expr, void *data)
+{
+	int ret;
+	struct eval_args *args = data;
+
+	if (expr != NULL && expr->ops->eval != NULL) {
+		ret = expr->ops->eval(expr, args->data, args->size, NULL, NULL);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static void sample_callback(void *ctx __maybe_unused, int cpu __maybe_unused,
+			    void *data, __u32 size __maybe_unused)
+{
+	struct __output_data_payload *sample_data = data;
+	struct eval_args args = {
+		.data = sample_data->__data,
+		.size = sample_data->__size,
+	};
+	(void)event_actions__for_each_expr(do_sample_handler, &args, false);
+}
+
+static void lost_callback(void *ctx __maybe_unused, int cpu, __u64 cnt)
+{
+	fprintf(stderr, "Lost %llu events on CPU #%d\n", cnt, cpu);
+}
+
+static int bpf_record_prepare(const char *subsystem, const char *event_name)
+{
+	int ret, map_fd;
+
+	skel = record_action_bpf__open();
+	if (!skel) {
+		pr_err("open record-action BPF skeleton failed\n");
+		return -1;
+	}
+
+	set_max_rlimit();
+
+	ret = bpf_program__set_type(skel->progs.sample_output, BPF_PROG_TYPE_TRACEPOINT);
+	if (ret) {
+		pr_err("set type record-action BPF skeleton failed\n");
+		goto out;
+	}
+
+	ret = record_action_bpf__load(skel);
+	if (ret) {
+		pr_err("load record-action BPF skeleton failed\n");
+		goto out;
+	}
+
+	ret = bpf_set_output_format(skel->bss->output_formats);
+	if (ret)
+		goto out;
+
+	map_fd = bpf_map__fd(skel->maps.__sample_data__);
+	if (map_fd < 0) {
+		pr_err("map fd record-action BPF skeleton failed\n");
+		goto out;
+	}
+
+	skel->links.sample_output = bpf_program__attach_tracepoint(
+		skel->progs.sample_output, subsystem, event_name);
+	if (IS_ERR(skel->links.sample_output)) {
+		pr_err("attach record-action BPF skeleton failed\n");
+		goto out;
+	}
+
+	pb = perf_buffer__new(map_fd, 8, sample_callback,
+			      lost_callback, NULL, NULL);
+	ret = libbpf_get_error(pb);
+	if (ret) {
+		pr_err("setup record-action perf_buffer failed: %d\n", ret);
+		goto out;
+	}
+
+	return 0;
+
+out:
+	record_action_bpf__destroy(skel);
+	return -1;
+}
+
+static inline void bpf_record_start(void)
+{
+	skel->bss->enabled = 1;
+}
+
+static inline void bpf_record_stop(void)
 {
+	skel->bss->enabled = 0;
+}
+
+static volatile int done;
+static volatile sig_atomic_t child_finished;
+static void sig_handler(int sig)
+{
+	if (sig == SIGCHLD)
+		child_finished = 1;
+
+	done = 1;
+}
+
+static bool is_bpf_record_supported(struct evlist *evlist,
+				    char **subsystem, char **event_name)
+{
+	struct evsel *evsel;
+
+	if (evlist == NULL) {
+		pr_err("--action option should follow a tracer option\n");
+		return false;
+	}
+
+	/* only one fixed bpf prog and can only be attached to one event. */
+	if (evlist->core.nr_entries > 1) {
+		pr_err("too many events for specified action\n");
+		return false;
+	}
+
+	evsel = evlist__last(evlist);
+	if (evsel == NULL) {
+		pr_err("evlist for bpf record action is empty\n");
+		return false;
+	}
+
+	if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) {
+		pr_err("bpf record action only supports specifying for tracepoint tracer\n");
+		return false;
+	}
+
+	*subsystem = strtok_r(evsel->name, ":", event_name);
+	if (*subsystem == NULL || event_name == NULL) {
+		pr_err("bpf record action tracepoint name format incorrect\n");
+		return false;
+	}
+
+	return true;
+}
+
+int bpf_perf_record(struct evlist *evlist, int argc, const char **argv)
+{
+	int ret;
+	char *subsystem, *event_name;
+	struct target target = {
+		.system_wide = true,
+	};
+
+	if (!is_bpf_record_supported(evlist, &subsystem, &event_name))
+		goto out_free_event_actions;
+
+	ret = bpf_record_prepare(subsystem, event_name);
+	if (ret)
+		goto out_free_event_actions;
+
+	signal(SIGINT, sig_handler);
+	signal(SIGTERM, sig_handler);
+	signal(SIGCHLD, sig_handler);
+
+	if (argc > 0) {
+		ret = evlist__prepare_workload(evlist, &target, argv, false, NULL);
+		if (ret < 0) {
+			pr_err("evlist workload create failed\n");
+			goto out_destroy_record_action_bpf;
+		}
+	}
+
+	bpf_record_start();
+	evlist__start_workload(evlist);
+
+	while ((ret = perf_buffer__poll(pb, 1000)) >= 0) {
+		if (done == 1)
+			break;
+	}
+
+	bpf_record_stop();
+
+	if (argc > 0) {
+		int exit_status;
+
+		if (!child_finished)
+			kill(evlist->workload.pid, SIGTERM);
+
+		wait(&exit_status);
+	}
+
+out_destroy_record_action_bpf:
+	record_action_bpf__destroy(skel);
+out_free_event_actions:
 	event_actions__free();
 	return 0;
 }
diff --git a/tools/perf/util/record_action.h b/tools/perf/util/record_action.h
index 289be4befa97..6cd578af2ccb 100644
--- a/tools/perf/util/record_action.h
+++ b/tools/perf/util/record_action.h
@@ -7,8 +7,9 @@
 
 #ifdef HAVE_BPF_SKEL
 
-int bpf_perf_record(struct evlist *evlist, int argc, const char **argv);
+int bpf_perf_record_init(void);
 
+int bpf_perf_record(struct evlist *evlist, int argc, const char **argv);
 
 #else /* !HAVE_BPF_SKEL */
 
@@ -19,6 +20,11 @@ static inline int bpf_perf_record(struct evlist *evlist __maybe_unused,
 	return -EOPNOTSUPP;
 }
 
+static inline int bpf_perf_record_init(void)
+{
+	return 0;
+}
+
 #endif /* !HAVE_BPF_SKEL */
 
 #endif /* __PERF_UTIL_RECORD_ACTION_H_ */
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [RFC 08/12] perf event action: Add parsing builtin cpu expr support
  2024-11-28 13:35 [RFC 00/12] perf record: Add event action support Yang Jihong
                   ` (6 preceding siblings ...)
  2024-11-28 13:35 ` [RFC 07/12] perf event action: Add parsing builtin " Yang Jihong
@ 2024-11-28 13:35 ` Yang Jihong
  2024-11-28 13:35 ` [RFC 09/12] perf event action: Add parsing builtin pid " Yang Jihong
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 27+ messages in thread
From: Yang Jihong @ 2024-11-28 13:35 UTC (permalink / raw)
  To: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
	jolsa, irogers, adrian.hunter, kan.liang, james.clark,
	linux-perf-users, linux-kernel
  Cc: yangjihong

Support parsing of builtin cpu expression.

Testcase:
  # perf record -e sched:sched_switch --action 'print("cpu = %d\n", cpu)'
  cpu = 7
  cpu = 7
  cpu = 7
  cpu = 7
  cpu = 7
  cpu = 7
  cpu = 7
  cpu = 7
  cpu = 7
  ...
Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
---
 tools/perf/util/bpf_skel/bpf_record_action.h |  1 +
 tools/perf/util/bpf_skel/record_action.bpf.c | 16 ++++++++++++++++
 tools/perf/util/parse-action.c               |  1 +
 tools/perf/util/parse-action.h               |  1 +
 tools/perf/util/parse-action.l               |  1 +
 tools/perf/util/record_action.c              |  7 +++++--
 6 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/bpf_skel/bpf_record_action.h b/tools/perf/util/bpf_skel/bpf_record_action.h
index ee4d03848e58..91a307b89f85 100644
--- a/tools/perf/util/bpf_skel/bpf_record_action.h
+++ b/tools/perf/util/bpf_skel/bpf_record_action.h
@@ -7,6 +7,7 @@
 #define __OUTPUT_FORMATS_MAX_NUM 8
 
 enum __output_format_type {
+	__OUTPUT_FORMAT_TYPE_CPU,
 	__OUTPUT_FORMAT_TYPE_MAX,
 };
 
diff --git a/tools/perf/util/bpf_skel/record_action.bpf.c b/tools/perf/util/bpf_skel/record_action.bpf.c
index 424fa8c3e6f1..4b7698554ead 100644
--- a/tools/perf/util/bpf_skel/record_action.bpf.c
+++ b/tools/perf/util/bpf_skel/record_action.bpf.c
@@ -35,10 +35,22 @@ static inline struct __output_data_payload *sample_data_payload(void)
 	return bpf_map_lookup_elem(&sample_data_tmp, &key);
 }
 
+static inline int output_cpu(__u8 *data, int size)
+{
+	__u32 *cpu = (__u32 *)data;
+
+	if (size < sizeof(__u32))
+		return -1;
+
+	*cpu = bpf_get_smp_processor_id();
+	return sizeof(__u32);
+}
+
 SEC("xxx")
 int sample_output(u64 *ctx)
 {
 	struct __output_data_payload *sample;
+	__u8 *data;
 	int i;
 	int total = 0;
 	int ret = 0;
@@ -50,8 +62,12 @@ int sample_output(u64 *ctx)
 	if (!sample)
 		return 0;
 
+	data = sample->__data;
 	for (i = 0; i < output_format_num && i < __OUTPUT_FORMATS_MAX_NUM; i++) {
 		switch (output_formats[i]) {
+		case __OUTPUT_FORMAT_TYPE_CPU:
+			ret = output_cpu(data + total, __OUTPUT_DATA_MAX_SIZE - total);
+			break;
 		default:
 			ret = -1;
 			break;
diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
index e6299de99bc5..2bb32cc16ae7 100644
--- a/tools/perf/util/parse-action.c
+++ b/tools/perf/util/parse-action.c
@@ -12,6 +12,7 @@
  *   - call:
  *     - print
  *   - builtin:
+ *     - cpu
  */
 
 #include <regex.h>
diff --git a/tools/perf/util/parse-action.h b/tools/perf/util/parse-action.h
index b1a33a77c558..ee949ed07111 100644
--- a/tools/perf/util/parse-action.h
+++ b/tools/perf/util/parse-action.h
@@ -27,6 +27,7 @@ enum evtact_expr_call_type {
 };
 
 enum evtact_expr_builtin_type {
+	EVTACT_EXPR_BUILTIN_TYPE_CPU,
 	EVTACT_EXPR_BUILTIN_TYPE_MAX,
 };
 
diff --git a/tools/perf/util/parse-action.l b/tools/perf/util/parse-action.l
index 1c729b7a0248..dab8066a1038 100644
--- a/tools/perf/util/parse-action.l
+++ b/tools/perf/util/parse-action.l
@@ -103,6 +103,7 @@ ident		[_a-zA-Z][_a-zA-Z0-9]*
 {num_hex}	{ return value(16); }
 {space}		{ }
 
+cpu		{ return builtin(EVTACT_EXPR_BUILTIN_TYPE_CPU);  }
 
 print		{ return call(EVTACT_EXPR_CALL_TYPE_PRINT); }
 
diff --git a/tools/perf/util/record_action.c b/tools/perf/util/record_action.c
index 634d6b199bae..15e8b1740e32 100644
--- a/tools/perf/util/record_action.c
+++ b/tools/perf/util/record_action.c
@@ -86,13 +86,16 @@ int bpf_perf_record_init(void)
 }
 
 static int set_expr_builtin_output_format(struct evtact_expr *expr,
-					  u32 opcode, int *offset,
-					  int *format __maybe_unused)
+					  u32 opcode, int *offset, int *format)
 {
 	int size = 0;
 	struct expr_builtin_output_priv *priv = expr->priv;
 
 	switch (opcode) {
+	case EVTACT_EXPR_BUILTIN_TYPE_CPU:
+		*format = __OUTPUT_FORMAT_TYPE_CPU;
+		size = sizeof(u32);
+		break;
 	default:
 		pr_err("set expr builtin output format unknown opcode: %u\n", opcode);
 		return -1;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [RFC 09/12] perf event action: Add parsing builtin pid expr support
  2024-11-28 13:35 [RFC 00/12] perf record: Add event action support Yang Jihong
                   ` (7 preceding siblings ...)
  2024-11-28 13:35 ` [RFC 08/12] perf event action: Add parsing builtin cpu " Yang Jihong
@ 2024-11-28 13:35 ` Yang Jihong
  2024-11-28 13:35 ` [RFC 10/12] perf event action: Add parsing builtin tid " Yang Jihong
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 27+ messages in thread
From: Yang Jihong @ 2024-11-28 13:35 UTC (permalink / raw)
  To: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
	jolsa, irogers, adrian.hunter, kan.liang, james.clark,
	linux-perf-users, linux-kernel
  Cc: yangjihong

Support parsing of builtin pid expression.

Testcase:
  # perf record -e sched:sched_switch --action 'print("pid = %d\n", pid)'
  pid = 132784
  pid = 0
  pid = 132784
  pid = 132778
  pid = 0
  pid = 0
  pid = 132784
  pid = 0
  pid = 132784
  pid = 0
  pid = 132784
  pid = 0
  pid = 132784
  ...
Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
---
 tools/perf/util/bpf_skel/bpf_record_action.h |  1 +
 tools/perf/util/bpf_skel/record_action.bpf.c | 14 ++++++++++++++
 tools/perf/util/parse-action.c               |  1 +
 tools/perf/util/parse-action.h               |  1 +
 tools/perf/util/parse-action.l               |  1 +
 tools/perf/util/record_action.c              |  4 ++++
 6 files changed, 22 insertions(+)

diff --git a/tools/perf/util/bpf_skel/bpf_record_action.h b/tools/perf/util/bpf_skel/bpf_record_action.h
index 91a307b89f85..a4817d62bce8 100644
--- a/tools/perf/util/bpf_skel/bpf_record_action.h
+++ b/tools/perf/util/bpf_skel/bpf_record_action.h
@@ -8,6 +8,7 @@
 
 enum __output_format_type {
 	__OUTPUT_FORMAT_TYPE_CPU,
+	__OUTPUT_FORMAT_TYPE_PID,
 	__OUTPUT_FORMAT_TYPE_MAX,
 };
 
diff --git a/tools/perf/util/bpf_skel/record_action.bpf.c b/tools/perf/util/bpf_skel/record_action.bpf.c
index 4b7698554ead..fd44bb3da663 100644
--- a/tools/perf/util/bpf_skel/record_action.bpf.c
+++ b/tools/perf/util/bpf_skel/record_action.bpf.c
@@ -46,6 +46,17 @@ static inline int output_cpu(__u8 *data, int size)
 	return sizeof(__u32);
 }
 
+static inline int output_pid(__u8 *data, int size)
+{
+	__u32 *pid = (__u32 *)data;
+
+	if (size < sizeof(__u32))
+		return -1;
+
+	*pid = bpf_get_current_pid_tgid() >> 32;
+	return sizeof(__u32);
+}
+
 SEC("xxx")
 int sample_output(u64 *ctx)
 {
@@ -68,6 +79,9 @@ int sample_output(u64 *ctx)
 		case __OUTPUT_FORMAT_TYPE_CPU:
 			ret = output_cpu(data + total, __OUTPUT_DATA_MAX_SIZE - total);
 			break;
+		case __OUTPUT_FORMAT_TYPE_PID:
+			ret = output_pid(data + total, __OUTPUT_DATA_MAX_SIZE - total);
+			break;
 		default:
 			ret = -1;
 			break;
diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
index 2bb32cc16ae7..0ae998a5d286 100644
--- a/tools/perf/util/parse-action.c
+++ b/tools/perf/util/parse-action.c
@@ -13,6 +13,7 @@
  *     - print
  *   - builtin:
  *     - cpu
+ *     - pid
  */
 
 #include <regex.h>
diff --git a/tools/perf/util/parse-action.h b/tools/perf/util/parse-action.h
index ee949ed07111..e74d3b4e5de8 100644
--- a/tools/perf/util/parse-action.h
+++ b/tools/perf/util/parse-action.h
@@ -28,6 +28,7 @@ enum evtact_expr_call_type {
 
 enum evtact_expr_builtin_type {
 	EVTACT_EXPR_BUILTIN_TYPE_CPU,
+	EVTACT_EXPR_BUILTIN_TYPE_PID,
 	EVTACT_EXPR_BUILTIN_TYPE_MAX,
 };
 
diff --git a/tools/perf/util/parse-action.l b/tools/perf/util/parse-action.l
index dab8066a1038..5a5bbe3e1f94 100644
--- a/tools/perf/util/parse-action.l
+++ b/tools/perf/util/parse-action.l
@@ -104,6 +104,7 @@ ident		[_a-zA-Z][_a-zA-Z0-9]*
 {space}		{ }
 
 cpu		{ return builtin(EVTACT_EXPR_BUILTIN_TYPE_CPU);  }
+pid		{ return builtin(EVTACT_EXPR_BUILTIN_TYPE_PID);  }
 
 print		{ return call(EVTACT_EXPR_CALL_TYPE_PRINT); }
 
diff --git a/tools/perf/util/record_action.c b/tools/perf/util/record_action.c
index 15e8b1740e32..1d7b603713d1 100644
--- a/tools/perf/util/record_action.c
+++ b/tools/perf/util/record_action.c
@@ -96,6 +96,10 @@ static int set_expr_builtin_output_format(struct evtact_expr *expr,
 		*format = __OUTPUT_FORMAT_TYPE_CPU;
 		size = sizeof(u32);
 		break;
+	case EVTACT_EXPR_BUILTIN_TYPE_PID:
+		*format = __OUTPUT_FORMAT_TYPE_PID;
+		size = sizeof(u32);
+		break;
 	default:
 		pr_err("set expr builtin output format unknown opcode: %u\n", opcode);
 		return -1;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [RFC 10/12] perf event action: Add parsing builtin tid expr support
  2024-11-28 13:35 [RFC 00/12] perf record: Add event action support Yang Jihong
                   ` (8 preceding siblings ...)
  2024-11-28 13:35 ` [RFC 09/12] perf event action: Add parsing builtin pid " Yang Jihong
@ 2024-11-28 13:35 ` Yang Jihong
  2024-11-28 13:35 ` [RFC 11/12] perf event action: Add parsing builtin comm " Yang Jihong
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 27+ messages in thread
From: Yang Jihong @ 2024-11-28 13:35 UTC (permalink / raw)
  To: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
	jolsa, irogers, adrian.hunter, kan.liang, james.clark,
	linux-perf-users, linux-kernel
  Cc: yangjihong

Support parsing of builtin tid expression.

Testcase:
  # perf record -e sched:sched_switch --action 'print("tid = %d\n", tid)' true
  tid = 134891
  tid = 0
  tid = 134891
  tid = 0
  tid = 0
  tid = 134891
  tid = 132778
  tid = 134891
  tid = 132778
  tid = 134891
  tid = 132778
  tid = 134891
  tid = 132778
  tid = 134891
  tid = 132778
  ...
Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
---
 tools/perf/util/bpf_skel/bpf_record_action.h |  1 +
 tools/perf/util/bpf_skel/record_action.bpf.c | 14 ++++++++++++++
 tools/perf/util/parse-action.c               |  1 +
 tools/perf/util/parse-action.h               |  1 +
 tools/perf/util/parse-action.l               |  1 +
 tools/perf/util/record_action.c              |  4 ++++
 6 files changed, 22 insertions(+)

diff --git a/tools/perf/util/bpf_skel/bpf_record_action.h b/tools/perf/util/bpf_skel/bpf_record_action.h
index a4817d62bce8..1d2ffa3c260f 100644
--- a/tools/perf/util/bpf_skel/bpf_record_action.h
+++ b/tools/perf/util/bpf_skel/bpf_record_action.h
@@ -9,6 +9,7 @@
 enum __output_format_type {
 	__OUTPUT_FORMAT_TYPE_CPU,
 	__OUTPUT_FORMAT_TYPE_PID,
+	__OUTPUT_FORMAT_TYPE_TID,
 	__OUTPUT_FORMAT_TYPE_MAX,
 };
 
diff --git a/tools/perf/util/bpf_skel/record_action.bpf.c b/tools/perf/util/bpf_skel/record_action.bpf.c
index fd44bb3da663..b6049b9af055 100644
--- a/tools/perf/util/bpf_skel/record_action.bpf.c
+++ b/tools/perf/util/bpf_skel/record_action.bpf.c
@@ -57,6 +57,17 @@ static inline int output_pid(__u8 *data, int size)
 	return sizeof(__u32);
 }
 
+static inline int output_tid(__u8 *data, int size)
+{
+	__u32 *pid = (__u32 *)data;
+
+	if (size < sizeof(__u32))
+		return -1;
+
+	*pid = bpf_get_current_pid_tgid() & 0xffffffff;
+	return sizeof(__u32);
+}
+
 SEC("xxx")
 int sample_output(u64 *ctx)
 {
@@ -82,6 +93,9 @@ int sample_output(u64 *ctx)
 		case __OUTPUT_FORMAT_TYPE_PID:
 			ret = output_pid(data + total, __OUTPUT_DATA_MAX_SIZE - total);
 			break;
+		case __OUTPUT_FORMAT_TYPE_TID:
+			ret = output_tid(data + total, __OUTPUT_DATA_MAX_SIZE - total);
+			break;
 		default:
 			ret = -1;
 			break;
diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
index 0ae998a5d286..6928990c9106 100644
--- a/tools/perf/util/parse-action.c
+++ b/tools/perf/util/parse-action.c
@@ -14,6 +14,7 @@
  *   - builtin:
  *     - cpu
  *     - pid
+ *     - tid
  */
 
 #include <regex.h>
diff --git a/tools/perf/util/parse-action.h b/tools/perf/util/parse-action.h
index e74d3b4e5de8..817e2c4213e0 100644
--- a/tools/perf/util/parse-action.h
+++ b/tools/perf/util/parse-action.h
@@ -29,6 +29,7 @@ enum evtact_expr_call_type {
 enum evtact_expr_builtin_type {
 	EVTACT_EXPR_BUILTIN_TYPE_CPU,
 	EVTACT_EXPR_BUILTIN_TYPE_PID,
+	EVTACT_EXPR_BUILTIN_TYPE_TID,
 	EVTACT_EXPR_BUILTIN_TYPE_MAX,
 };
 
diff --git a/tools/perf/util/parse-action.l b/tools/perf/util/parse-action.l
index 5a5bbe3e1f94..2ada6b4bf091 100644
--- a/tools/perf/util/parse-action.l
+++ b/tools/perf/util/parse-action.l
@@ -105,6 +105,7 @@ ident		[_a-zA-Z][_a-zA-Z0-9]*
 
 cpu		{ return builtin(EVTACT_EXPR_BUILTIN_TYPE_CPU);  }
 pid		{ return builtin(EVTACT_EXPR_BUILTIN_TYPE_PID);  }
+tid		{ return builtin(EVTACT_EXPR_BUILTIN_TYPE_TID);  }
 
 print		{ return call(EVTACT_EXPR_CALL_TYPE_PRINT); }
 
diff --git a/tools/perf/util/record_action.c b/tools/perf/util/record_action.c
index 1d7b603713d1..f4db98b7df4d 100644
--- a/tools/perf/util/record_action.c
+++ b/tools/perf/util/record_action.c
@@ -100,6 +100,10 @@ static int set_expr_builtin_output_format(struct evtact_expr *expr,
 		*format = __OUTPUT_FORMAT_TYPE_PID;
 		size = sizeof(u32);
 		break;
+	case EVTACT_EXPR_BUILTIN_TYPE_TID:
+		*format = __OUTPUT_FORMAT_TYPE_TID;
+		size = sizeof(u32);
+		break;
 	default:
 		pr_err("set expr builtin output format unknown opcode: %u\n", opcode);
 		return -1;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [RFC 11/12] perf event action: Add parsing builtin comm expr support
  2024-11-28 13:35 [RFC 00/12] perf record: Add event action support Yang Jihong
                   ` (9 preceding siblings ...)
  2024-11-28 13:35 ` [RFC 10/12] perf event action: Add parsing builtin tid " Yang Jihong
@ 2024-11-28 13:35 ` Yang Jihong
  2024-11-28 13:35 ` [RFC 12/12] perf event action: Add parsing builtin time " Yang Jihong
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 27+ messages in thread
From: Yang Jihong @ 2024-11-28 13:35 UTC (permalink / raw)
  To: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
	jolsa, irogers, adrian.hunter, kan.liang, james.clark,
	linux-perf-users, linux-kernel
  Cc: yangjihong

Support parsing of builtin comm expression.

Testcase:
  # perf record -e sched:sched_switch --action 'print("comm = %s\n", comm)' true
  comm = perf
  comm = swapper/7
  comm = perf
  comm = swapper/7
  comm = swapper/1
  comm = perf
  comm = kworker/u40:0
  comm = perf
  comm = kworker/u40:0
  ...
Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
---
 tools/perf/util/bpf_skel/bpf_record_action.h |  1 +
 tools/perf/util/bpf_skel/record_action.bpf.c | 19 +++++++++++++++++++
 tools/perf/util/parse-action.c               |  1 +
 tools/perf/util/parse-action.h               |  1 +
 tools/perf/util/parse-action.l               |  1 +
 tools/perf/util/record_action.c              |  4 ++++
 6 files changed, 27 insertions(+)

diff --git a/tools/perf/util/bpf_skel/bpf_record_action.h b/tools/perf/util/bpf_skel/bpf_record_action.h
index 1d2ffa3c260f..325964b7aac9 100644
--- a/tools/perf/util/bpf_skel/bpf_record_action.h
+++ b/tools/perf/util/bpf_skel/bpf_record_action.h
@@ -10,6 +10,7 @@ enum __output_format_type {
 	__OUTPUT_FORMAT_TYPE_CPU,
 	__OUTPUT_FORMAT_TYPE_PID,
 	__OUTPUT_FORMAT_TYPE_TID,
+	__OUTPUT_FORMAT_TYPE_COMM,
 	__OUTPUT_FORMAT_TYPE_MAX,
 };
 
diff --git a/tools/perf/util/bpf_skel/record_action.bpf.c b/tools/perf/util/bpf_skel/record_action.bpf.c
index b6049b9af055..f809305b16be 100644
--- a/tools/perf/util/bpf_skel/record_action.bpf.c
+++ b/tools/perf/util/bpf_skel/record_action.bpf.c
@@ -68,6 +68,22 @@ static inline int output_tid(__u8 *data, int size)
 	return sizeof(__u32);
 }
 
+static inline int output_comm(__u8 *data, int size)
+{
+	struct task_struct *task = (struct task_struct *)bpf_get_current_task();
+
+	if (!task) {
+		data[0] = '\0';
+		return 0;
+	}
+
+	if (size < __TASK_COMM_MAX_SIZE)
+		return -1;
+
+	bpf_core_read_str(data, __TASK_COMM_MAX_SIZE, &task->comm);
+	return __TASK_COMM_MAX_SIZE;
+}
+
 SEC("xxx")
 int sample_output(u64 *ctx)
 {
@@ -96,6 +112,9 @@ int sample_output(u64 *ctx)
 		case __OUTPUT_FORMAT_TYPE_TID:
 			ret = output_tid(data + total, __OUTPUT_DATA_MAX_SIZE - total);
 			break;
+		case __OUTPUT_FORMAT_TYPE_COMM:
+			ret = output_comm(data + total, __OUTPUT_DATA_MAX_SIZE - total);
+			break;
 		default:
 			ret = -1;
 			break;
diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
index 6928990c9106..8bc1646abe27 100644
--- a/tools/perf/util/parse-action.c
+++ b/tools/perf/util/parse-action.c
@@ -15,6 +15,7 @@
  *     - cpu
  *     - pid
  *     - tid
+ *     - comm
  */
 
 #include <regex.h>
diff --git a/tools/perf/util/parse-action.h b/tools/perf/util/parse-action.h
index 817e2c4213e0..39fb521f3193 100644
--- a/tools/perf/util/parse-action.h
+++ b/tools/perf/util/parse-action.h
@@ -30,6 +30,7 @@ enum evtact_expr_builtin_type {
 	EVTACT_EXPR_BUILTIN_TYPE_CPU,
 	EVTACT_EXPR_BUILTIN_TYPE_PID,
 	EVTACT_EXPR_BUILTIN_TYPE_TID,
+	EVTACT_EXPR_BUILTIN_TYPE_COMM,
 	EVTACT_EXPR_BUILTIN_TYPE_MAX,
 };
 
diff --git a/tools/perf/util/parse-action.l b/tools/perf/util/parse-action.l
index 2ada6b4bf091..ddac305c1ca4 100644
--- a/tools/perf/util/parse-action.l
+++ b/tools/perf/util/parse-action.l
@@ -106,6 +106,7 @@ ident		[_a-zA-Z][_a-zA-Z0-9]*
 cpu		{ return builtin(EVTACT_EXPR_BUILTIN_TYPE_CPU);  }
 pid		{ return builtin(EVTACT_EXPR_BUILTIN_TYPE_PID);  }
 tid		{ return builtin(EVTACT_EXPR_BUILTIN_TYPE_TID);  }
+comm		{ return builtin(EVTACT_EXPR_BUILTIN_TYPE_COMM); }
 
 print		{ return call(EVTACT_EXPR_CALL_TYPE_PRINT); }
 
diff --git a/tools/perf/util/record_action.c b/tools/perf/util/record_action.c
index f4db98b7df4d..ead7eeffb7cc 100644
--- a/tools/perf/util/record_action.c
+++ b/tools/perf/util/record_action.c
@@ -104,6 +104,10 @@ static int set_expr_builtin_output_format(struct evtact_expr *expr,
 		*format = __OUTPUT_FORMAT_TYPE_TID;
 		size = sizeof(u32);
 		break;
+	case EVTACT_EXPR_BUILTIN_TYPE_COMM:
+		*format = __OUTPUT_FORMAT_TYPE_COMM;
+		size = __TASK_COMM_MAX_SIZE;
+		break;
 	default:
 		pr_err("set expr builtin output format unknown opcode: %u\n", opcode);
 		return -1;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [RFC 12/12] perf event action: Add parsing builtin time expr support
  2024-11-28 13:35 [RFC 00/12] perf record: Add event action support Yang Jihong
                   ` (10 preceding siblings ...)
  2024-11-28 13:35 ` [RFC 11/12] perf event action: Add parsing builtin comm " Yang Jihong
@ 2024-11-28 13:35 ` Yang Jihong
  2024-11-28 13:53 ` [RFC 00/12] perf record: Add event action support Adrian Hunter
  2024-11-28 20:14 ` Arnaldo Carvalho de Melo
  13 siblings, 0 replies; 27+ messages in thread
From: Yang Jihong @ 2024-11-28 13:35 UTC (permalink / raw)
  To: peterz, mingo, acme, namhyung, mark.rutland, alexander.shishkin,
	jolsa, irogers, adrian.hunter, kan.liang, james.clark,
	linux-perf-users, linux-kernel
  Cc: yangjihong

Support parsing of builtin time expression.

Testcase:
  # perf record -e sched:sched_switch --action 'print("time = %llu\n", time)' true
  time = 787661350481404
  time = 787661350483823
  time = 787661350493754
  time = 787661350623109
  time = 787661350482984
  time = 787661350642538
  time = 787661350647575
  time = 787661350649706
  time = 787661350650968
  time = 787661350653686
  time = 787661350654928
  ...
Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
---
 tools/perf/util/bpf_skel/bpf_record_action.h |  1 +
 tools/perf/util/bpf_skel/record_action.bpf.c | 14 ++++++++++++++
 tools/perf/util/parse-action.c               |  1 +
 tools/perf/util/parse-action.h               |  1 +
 tools/perf/util/parse-action.l               |  1 +
 tools/perf/util/record_action.c              |  4 ++++
 6 files changed, 22 insertions(+)

diff --git a/tools/perf/util/bpf_skel/bpf_record_action.h b/tools/perf/util/bpf_skel/bpf_record_action.h
index 325964b7aac9..e514ac14705e 100644
--- a/tools/perf/util/bpf_skel/bpf_record_action.h
+++ b/tools/perf/util/bpf_skel/bpf_record_action.h
@@ -11,6 +11,7 @@ enum __output_format_type {
 	__OUTPUT_FORMAT_TYPE_PID,
 	__OUTPUT_FORMAT_TYPE_TID,
 	__OUTPUT_FORMAT_TYPE_COMM,
+	__OUTPUT_FORMAT_TYPE_TIME,
 	__OUTPUT_FORMAT_TYPE_MAX,
 };
 
diff --git a/tools/perf/util/bpf_skel/record_action.bpf.c b/tools/perf/util/bpf_skel/record_action.bpf.c
index f809305b16be..ed36f121e90b 100644
--- a/tools/perf/util/bpf_skel/record_action.bpf.c
+++ b/tools/perf/util/bpf_skel/record_action.bpf.c
@@ -84,6 +84,17 @@ static inline int output_comm(__u8 *data, int size)
 	return __TASK_COMM_MAX_SIZE;
 }
 
+static inline int output_time(__u8 *data, int size)
+{
+	__u64 *ts = (__u64 *)data;
+
+	if (size < sizeof(__u64))
+		return -1;
+
+	*ts = bpf_ktime_get_ns();
+	return sizeof(__u64);
+}
+
 SEC("xxx")
 int sample_output(u64 *ctx)
 {
@@ -115,6 +126,9 @@ int sample_output(u64 *ctx)
 		case __OUTPUT_FORMAT_TYPE_COMM:
 			ret = output_comm(data + total, __OUTPUT_DATA_MAX_SIZE - total);
 			break;
+		case __OUTPUT_FORMAT_TYPE_TIME:
+			ret = output_time(data + total, __OUTPUT_DATA_MAX_SIZE - total);
+			break;
 		default:
 			ret = -1;
 			break;
diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
index 8bc1646abe27..4f40e4a24357 100644
--- a/tools/perf/util/parse-action.c
+++ b/tools/perf/util/parse-action.c
@@ -16,6 +16,7 @@
  *     - pid
  *     - tid
  *     - comm
+ *     - time
  */
 
 #include <regex.h>
diff --git a/tools/perf/util/parse-action.h b/tools/perf/util/parse-action.h
index 39fb521f3193..57df6f8294f5 100644
--- a/tools/perf/util/parse-action.h
+++ b/tools/perf/util/parse-action.h
@@ -31,6 +31,7 @@ enum evtact_expr_builtin_type {
 	EVTACT_EXPR_BUILTIN_TYPE_PID,
 	EVTACT_EXPR_BUILTIN_TYPE_TID,
 	EVTACT_EXPR_BUILTIN_TYPE_COMM,
+	EVTACT_EXPR_BUILTIN_TYPE_TIME,
 	EVTACT_EXPR_BUILTIN_TYPE_MAX,
 };
 
diff --git a/tools/perf/util/parse-action.l b/tools/perf/util/parse-action.l
index ddac305c1ca4..9029ac19c128 100644
--- a/tools/perf/util/parse-action.l
+++ b/tools/perf/util/parse-action.l
@@ -107,6 +107,7 @@ cpu		{ return builtin(EVTACT_EXPR_BUILTIN_TYPE_CPU);  }
 pid		{ return builtin(EVTACT_EXPR_BUILTIN_TYPE_PID);  }
 tid		{ return builtin(EVTACT_EXPR_BUILTIN_TYPE_TID);  }
 comm		{ return builtin(EVTACT_EXPR_BUILTIN_TYPE_COMM); }
+time		{ return builtin(EVTACT_EXPR_BUILTIN_TYPE_TIME); }
 
 print		{ return call(EVTACT_EXPR_CALL_TYPE_PRINT); }
 
diff --git a/tools/perf/util/record_action.c b/tools/perf/util/record_action.c
index ead7eeffb7cc..4de6afcbfb26 100644
--- a/tools/perf/util/record_action.c
+++ b/tools/perf/util/record_action.c
@@ -108,6 +108,10 @@ static int set_expr_builtin_output_format(struct evtact_expr *expr,
 		*format = __OUTPUT_FORMAT_TYPE_COMM;
 		size = __TASK_COMM_MAX_SIZE;
 		break;
+	case EVTACT_EXPR_BUILTIN_TYPE_TIME:
+		*format = __OUTPUT_FORMAT_TYPE_TIME;
+		size = sizeof(u64);
+		break;
 	default:
 		pr_err("set expr builtin output format unknown opcode: %u\n", opcode);
 		return -1;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [RFC 00/12] perf record: Add event action support
  2024-11-28 13:35 [RFC 00/12] perf record: Add event action support Yang Jihong
                   ` (11 preceding siblings ...)
  2024-11-28 13:35 ` [RFC 12/12] perf event action: Add parsing builtin time " Yang Jihong
@ 2024-11-28 13:53 ` Adrian Hunter
  2024-12-04  8:07   ` [External] " Yang Jihong
  2024-11-28 20:14 ` Arnaldo Carvalho de Melo
  13 siblings, 1 reply; 27+ messages in thread
From: Adrian Hunter @ 2024-11-28 13:53 UTC (permalink / raw)
  To: Yang Jihong, peterz, mingo, acme, namhyung, mark.rutland,
	alexander.shishkin, jolsa, irogers, kan.liang, james.clark,
	linux-perf-users, linux-kernel

On 28/11/24 15:35, Yang Jihong wrote:
> In perf-record, when an event is triggered, default behavior is to
> save sample data to perf.data. Sometimes, we may just want to do
> some lightweight actions, such as printing a log.

Why not just pipe 'perf record' to 'perf script'?

# perf record -e sched:sched_switch | perf script | head
            perf  768231 [000] 2318380.474267: sched:sched_switch: perf:768231 [120] R ==> migration/0:18 [0]
     migration/0      18 [000] 2318380.474294: sched:sched_switch: migration/0:18 [0] S ==> swapper/0:0 [120]
            perf  768231 [001] 2318380.474353: sched:sched_switch: perf:768231 [120] R ==> migration/1:23 [0]
     migration/1      23 [001] 2318380.474382: sched:sched_switch: migration/1:23 [0] S ==> swapper/1:0 [120]
            perf  768231 [002] 2318380.474477: sched:sched_switch: perf:768231 [120] R ==> migration/2:29 [0]
     migration/2      29 [002] 2318380.474503: sched:sched_switch: migration/2:29 [0] S ==> swapper/2:0 [120]
            perf  768231 [003] 2318380.474513: sched:sched_switch: perf:768231 [120] R ==> migration/3:35 [0]
     migration/3      35 [003] 2318380.474523: sched:sched_switch: migration/3:35 [0] S ==> swapper/3:0 [120]
            perf  768231 [004] 2318380.474534: sched:sched_switch: perf:768231 [120] R ==> migration/4:41 [0]
     migration/4      41 [004] 2318380.474541: sched:sched_switch: migration/4:41 [0] S ==> swapper/4:0 [120]

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [RFC 00/12] perf record: Add event action support
  2024-11-28 13:35 [RFC 00/12] perf record: Add event action support Yang Jihong
                   ` (12 preceding siblings ...)
  2024-11-28 13:53 ` [RFC 00/12] perf record: Add event action support Adrian Hunter
@ 2024-11-28 20:14 ` Arnaldo Carvalho de Melo
  2024-12-02 21:46   ` Namhyung Kim
  2024-12-04  8:21   ` Yang Jihong
  13 siblings, 2 replies; 27+ messages in thread
From: Arnaldo Carvalho de Melo @ 2024-11-28 20:14 UTC (permalink / raw)
  To: Yang Jihong
  Cc: peterz, mingo, namhyung, mark.rutland, alexander.shishkin, jolsa,
	irogers, adrian.hunter, kan.liang, james.clark, linux-perf-users,
	linux-kernel

On Thu, Nov 28, 2024 at 09:35:41PM +0800, Yang Jihong wrote:
> In perf-record, when an event is triggered, default behavior is to
> save sample data to perf.data. Sometimes, we may just want to do
> some lightweight actions, such as printing a log.

> Based on this requirement, add the --action option to the event to
> specify the behavior when the event occurs.

'perf record' is centered on saving data to disk without processing
events, while it has sideband events for some needs, like processing BPF
related events (PERF_RECORD_BPF_EVENT to catch PERF_BPF_EVENT_PROG_LOAD
and UNLOAD), doing things in a "live" way as your patchkit does seems
more appropriate to do in 'perf trace' :-)

I'll take a look at the rest of the patch series.

- Arnaldo
 
> This patchset uses bpf prog to attach to tracepoint event, and save sample
> to bpf perf_event ringbuffer in handler. perf-tool read the data and run actions.
> 
> Currently only one call is supported, that is, print(),
> and some commonly used builtin variables are also supported.
> 
> For example:
> 
>   # perf record -e sched:sched_switch --action 'print("[%03d][%llu]comm=%s, pid=%d, tid=%d\n", cpu, time, comm, pid, tid)' true
>   [003][795464100275136]comm=perf, pid=141580, tid=141580
>   [003][795464100278234]comm=swapper/3, pid=0, tid=0
>   [003][795464100288984]comm=perf, pid=141580, tid=141580
>   [003][795464100457865]comm=swapper/3, pid=0, tid=0
>   [003][795464100485547]comm=perf, pid=141580, tid=141580
>   [003][795464100491398]comm=kworker/u36:1, pid=139834, tid=139834
>   [003][795464100493647]comm=perf, pid=141580, tid=141580
>   [003][795464100494967]comm=kworker/u36:1, pid=139834, tid=139834
>   [003][795464100498146]comm=perf, pid=141580, tid=141580
>   ...
> 
>   # perf record -e cycles --action 'print("test\n");' true
>   bpf record action only supports specifying for tracepoint tracer
> 
>   # perf record -e sched:sched_switch --action 'print("[%llu]comm=%s, cpu=%d, pid=%d, tid=%d\n", time, comm, cpu, pid)' true
>   print() arguments number for format string mismatch: 5 expected, 4 provided
>   parse action option failed
> 
>    Usage: perf record [<options>] [<command>]
>       or: perf record [<options>] -- <command> [<options>]
> 
>           --action <action>
>                             event action
> 
>   # perf record -e sched:sched_switch --action 'print("test\n");' true
>   test
>   test
>   test
>   test
>   test
>   test
>   test
>   test
>   test
>   test
>   ...
> 
> This patchset implements simple features and can be extended as needed.
> 
> TODO LIST:
> 1. Support common operations such as logical operations and bit operations
> 2. Support other calls such as dumpstack(), count()
> 3. Support specify actions for kprobe events
> 4. For builds that disable bpf_skel, support real-time parsing of perf record mmap ringbuffer data (similar to perf top)
> 5. Link libllvm to support dynamic generation of bpf progs
> 
> Yang Jihong (12):
>   perf record: Add event action support
>   perf event action: Add parsing const expr support
>   perf event action: Add parsing const integer expr support
>   perf event action: Add parsing const string expr support
>   perf event action: Add parsing call expr support
>   perf event action: Add parsing print() call expr support
>   perf event action: Add parsing builtin expr support
>   perf event action: Add parsing builtin cpu expr support
>   perf event action: Add parsing builtin pid expr support
>   perf event action: Add parsing builtin tid expr support
>   perf event action: Add parsing builtin comm expr support
>   perf event action: Add parsing builtin time expr support
> 
>  tools/perf/Documentation/perf-record.txt     |   8 +
>  tools/perf/Makefile.perf                     |   1 +
>  tools/perf/builtin-record.c                  |  31 +
>  tools/perf/util/Build                        |  18 +
>  tools/perf/util/bpf_skel/bpf_record_action.h |  24 +
>  tools/perf/util/bpf_skel/record_action.bpf.c | 151 ++++
>  tools/perf/util/parse-action.c               | 729 +++++++++++++++++++
>  tools/perf/util/parse-action.h               |  98 +++
>  tools/perf/util/parse-action.l               | 190 +++++
>  tools/perf/util/parse-action.y               | 156 ++++
>  tools/perf/util/record_action.c              | 380 ++++++++++
>  tools/perf/util/record_action.h              |  30 +
>  12 files changed, 1816 insertions(+)
>  create mode 100644 tools/perf/util/bpf_skel/bpf_record_action.h
>  create mode 100644 tools/perf/util/bpf_skel/record_action.bpf.c
>  create mode 100644 tools/perf/util/parse-action.c
>  create mode 100644 tools/perf/util/parse-action.h
>  create mode 100644 tools/perf/util/parse-action.l
>  create mode 100644 tools/perf/util/parse-action.y
>  create mode 100644 tools/perf/util/record_action.c
>  create mode 100644 tools/perf/util/record_action.h
> 
> -- 
> 2.25.1

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [RFC 01/12] perf record: Add event action support
  2024-11-28 13:35 ` [RFC 01/12] " Yang Jihong
@ 2024-11-28 20:19   ` Arnaldo Carvalho de Melo
  2024-12-04  8:24     ` [External] " Yang Jihong
  0 siblings, 1 reply; 27+ messages in thread
From: Arnaldo Carvalho de Melo @ 2024-11-28 20:19 UTC (permalink / raw)
  To: Yang Jihong
  Cc: peterz, mingo, namhyung, mark.rutland, alexander.shishkin, jolsa,
	irogers, adrian.hunter, kan.liang, james.clark, linux-perf-users,
	linux-kernel

On Thu, Nov 28, 2024 at 09:35:42PM +0800, Yang Jihong wrote:
> In perf-record, when an event is triggered, default behavior is to
> save sample data to perf.data. Sometimes, we may just want to do
> some lightweight actions, such as printing a log.
> 
> Based on this requirement, add the --action option to the event to
> specify the behavior when the event occurs.
> 
> Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
> ---
>  tools/perf/Documentation/perf-record.txt |   8 +
>  tools/perf/builtin-record.c              |  31 +++
>  tools/perf/util/Build                    |  18 ++
>  tools/perf/util/parse-action.c           | 230 +++++++++++++++++++++++
>  tools/perf/util/parse-action.h           |  75 ++++++++
>  tools/perf/util/parse-action.l           |  40 ++++
>  tools/perf/util/parse-action.y           |  82 ++++++++
>  tools/perf/util/record_action.c          |  15 ++
>  tools/perf/util/record_action.h          |  24 +++
>  9 files changed, 523 insertions(+)
>  create mode 100644 tools/perf/util/parse-action.c
>  create mode 100644 tools/perf/util/parse-action.h
>  create mode 100644 tools/perf/util/parse-action.l
>  create mode 100644 tools/perf/util/parse-action.y
>  create mode 100644 tools/perf/util/record_action.c
>  create mode 100644 tools/perf/util/record_action.h
> 
> diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
> index 242223240a08..d0d9e0f69f3d 100644
> --- a/tools/perf/Documentation/perf-record.txt
> +++ b/tools/perf/Documentation/perf-record.txt
> @@ -833,6 +833,14 @@ filtered through the mask provided by -C option.
>  	Prepare BPF filter to be used by regular users.  The action should be
>  	either "pin" or "unpin".  The filter can be used after it's pinned.
>  
> +--action=<action>::
> +	Actions are the programs that run when the sampling event is triggered.
> +	The action is a list of expressions separated by semicolons (;).
> +	The sample data is saved by bpf prog attached by the event.
> +	The call currently supported is print(); some commonly used built-in special
> +	variables are also supported
> +	For example:
> +	  # perf record -e sched:sched_switch --action 'print("[%llu]comm=%s, cpu=%d, pid=%d, tid=%d\n", time, comm, cpu, pid, tid)' true

But at this point in the series this isn't available, right? 

I.e. when testing this specific patch I can't follow what the
documentation above says and expect anything, right? It will just fail?

>  include::intel-hybrid.txt[]
>  
> diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
> index f83252472921..108d98706873 100644
> --- a/tools/perf/builtin-record.c
> +++ b/tools/perf/builtin-record.c
> @@ -51,6 +51,10 @@
>  #include "util/clockid.h"
>  #include "util/off_cpu.h"
>  #include "util/bpf-filter.h"
> +#ifdef HAVE_BPF_SKEL
> +#include "util/parse-action.h"
> +#include "util/record_action.h"
> +#endif
>  #include "asm/bug.h"
>  #include "perf.h"
>  #include "cputopo.h"
> @@ -182,6 +186,7 @@ struct record {
>  	struct pollfd_index_map	*index_map;
>  	size_t			index_map_sz;
>  	size_t			index_map_cnt;
> +	bool			custom_action;
>  };
>  
>  static volatile int done;
> @@ -3316,6 +3321,23 @@ static int parse_record_synth_option(const struct option *opt,
>  	return 0;
>  }
>  
> +#ifdef HAVE_BPF_SKEL
> +static int parse_record_action_option(const struct option *opt,
> +				      const char *str,
> +				      int unset __maybe_unused)
> +{
> +	int ret;
> +	struct record *rec = (struct record *)opt->value;

There should be no need for casting above, opt->value is void *.

> +
> +	ret = parse_record_action(rec->evlist, str);
> +	if (ret)
> +		return ret;
> +
> +	rec->custom_action = true;
> +	return 0;
> +}
> +#endif
> +
>  /*
>   * XXX Ideally would be local to cmd_record() and passed to a record__new
>   * because we need to have access to it in record__exit, that is called
> @@ -3564,6 +3586,9 @@ static struct option __record_options[] = {
>  	OPT_BOOLEAN(0, "off-cpu", &record.off_cpu, "Enable off-cpu analysis"),
>  	OPT_STRING(0, "setup-filter", &record.filter_action, "pin|unpin",
>  		   "BPF filter action"),
> +#ifdef HAVE_BPF_SKEL
> +	OPT_CALLBACK(0, "action", &record, "action", "event action", parse_record_action_option),
> +#endif
>  	OPT_END()
>  };
>  
> @@ -4001,6 +4026,12 @@ int cmd_record(int argc, const char **argv)
>  	if (quiet)
>  		perf_quiet_option();
>  
> +#ifdef HAVE_BPF_SKEL
> +	/* Currently, event actions only supported using bpf prog. */
> +	if (rec->custom_action)
> +		return bpf_perf_record(rec->evlist, argc, argv);
> +#endif
> +
>  	err = symbol__validate_sym_arguments();
>  	if (err)
>  		return err;
> diff --git a/tools/perf/util/Build b/tools/perf/util/Build
> index c06d2ee9024c..db4c4cabc5f8 100644
> --- a/tools/perf/util/Build
> +++ b/tools/perf/util/Build
> @@ -249,6 +249,12 @@ perf-util-$(CONFIG_LIBBPF) += bpf-utils.o
>  
>  perf-util-$(CONFIG_LIBPFM4) += pfm.o
>  
> +# perf record event action
> +perf-util-$(CONFIG_PERF_BPF_SKEL) += parse-action.o
> +perf-util-$(CONFIG_PERF_BPF_SKEL) += parse-action-flex.o
> +perf-util-$(CONFIG_PERF_BPF_SKEL) += parse-action-bison.o
> +perf-util-$(CONFIG_PERF_BPF_SKEL) += record_action.o
> +
>  CFLAGS_config.o   += -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
>  
>  # avoid compiler warnings in 32-bit mode
> @@ -294,6 +300,16 @@ $(OUTPUT)util/bpf-filter-bison.c $(OUTPUT)util/bpf-filter-bison.h: util/bpf-filt
>  	$(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) $(BISON_FILE_PREFIX_MAP) \
>  		-o $(OUTPUT)util/bpf-filter-bison.c -p perf_bpf_filter_
>  
> +$(OUTPUT)util/parse-action-flex.c $(OUTPUT)util/parse-action-flex.h: util/parse-action.l $(OUTPUT)util/parse-action-bison.c util/parse-action.h
> +	$(call rule_mkdir)
> +	$(Q)$(call echo-cmd,flex)$(FLEX) -o $(OUTPUT)util/parse-action-flex.c \
> +		--header-file=$(OUTPUT)util/parse-action-flex.h $(PARSER_DEBUG_FLEX) $<
> +
> +$(OUTPUT)util/parse-action.c $(OUTPUT)util/parse-action-bison.h: util/parse-action.y util/parse-action.h
> +	$(call rule_mkdir)
> +	$(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) $(BISON_FILE_PREFIX_MAP) \
> +		-o $(OUTPUT)util/parse-action-bison.c -p parse_action_
> +
>  FLEX_VERSION := $(shell $(FLEX) --version | cut -d' ' -f2)
>  
>  FLEX_GE_260 := $(call version-ge3,$(FLEX_VERSION),2.6.0)
> @@ -345,11 +361,13 @@ CFLAGS_parse-events-flex.o  += $(flex_flags) -Wno-unused-label
>  CFLAGS_pmu-flex.o           += $(flex_flags)
>  CFLAGS_expr-flex.o          += $(flex_flags)
>  CFLAGS_bpf-filter-flex.o    += $(flex_flags)
> +CFLAGS_parse-action-flex.o  += $(flex_flags)
>  
>  CFLAGS_parse-events-bison.o += $(bison_flags)
>  CFLAGS_pmu-bison.o          += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
>  CFLAGS_expr-bison.o         += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
>  CFLAGS_bpf-filter-bison.o   += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
> +CFLAGS_parse-action-bison.o += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
>  
>  $(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c
>  $(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c
> diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
> new file mode 100644
> index 000000000000..01c8c7fdea59
> --- /dev/null
> +++ b/tools/perf/util/parse-action.c
> @@ -0,0 +1,230 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/**
> + * Generic actions for sampling events
> + * Actions are the programs that run when the sampling event is triggered.
> + * The action is a list of expressions separated by semicolons (;).
> + * Each action is an expression, added to actions_head node as list_head node.
> + */
> +
> +#include "util/debug.h"
> +#include "util/parse-action.h"
> +#include "util/parse-action-flex.h"
> +#include "util/parse-action-bison.h"
> +
> +static struct list_head actions_head = LIST_HEAD_INIT(actions_head);
> +
> +int event_actions__for_each_expr(int (*func)(struct evtact_expr *, void *arg),
> +				 void *arg, bool recursive)
> +{
> +	int ret;
> +	struct evtact_expr *expr, *opnd;
> +
> +	if (list_empty(&actions_head))
> +		return (*func)(NULL, arg);
> +
> +	list_for_each_entry(expr, &actions_head, list) {
> +		ret = (*func)(expr, arg);
> +		if (ret)
> +			return ret;
> +
> +		if (recursive && !list_empty(&expr->opnds)) {
> +			list_for_each_entry(opnd, &expr->opnds, list) {
> +				ret = (*func)(opnd, arg);
> +				if (ret)
> +					return ret;
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +int event_actions__for_each_expr_safe(int (*func)(struct evtact_expr *, void *arg),
> +				      void *arg, bool recursive)
> +{
> +	int ret;
> +	struct evtact_expr *expr, *tmp;
> +	struct evtact_expr *opnd, *opnd_tmp;
> +
> +	if (list_empty(&actions_head))
> +		return (*func)(NULL, arg);
> +
> +	list_for_each_entry_safe(expr, tmp, &actions_head, list) {
> +		ret = (*func)(expr, arg);
> +		if (ret)
> +			return ret;
> +
> +		if (recursive && !list_empty(&expr->opnds)) {
> +			list_for_each_entry_safe(opnd, opnd_tmp, &expr->opnds, list) {
> +				ret = (*func)(opnd, arg);
> +				if (ret)
> +					return ret;
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int parse_action_option(const char *str)
> +{
> +	int ret;
> +	YY_BUFFER_STATE buffer;
> +
> +	buffer = parse_action__scan_string(str);
> +	ret = parse_action_parse(&actions_head);
> +
> +	parse_action__flush_buffer(buffer);
> +	parse_action__delete_buffer(buffer);
> +	parse_action_lex_destroy();
> +
> +	return ret;
> +}
> +
> +int parse_record_action(struct evlist *evlist, const char *str)
> +{
> +	int ret;
> +
> +	if (evlist == NULL) {
> +		pr_err("--action option should follow a tracer option\n");
> +		return -1;
> +	}
> +
> +	ret = parse_action_option(str);
> +	if (ret) {
> +		event_actions__free();
> +		pr_err("parse action option failed\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int do_action_free(struct evtact_expr *action, void *data __maybe_unused)
> +{
> +	if (action == NULL)
> +		return 0;
> +
> +	list_del(&action->list);
> +	parse_action_expr__free(action);
> +	return 0;
> +}
> +
> +void event_actions__free(void)
> +{
> +	(void)event_actions__for_each_expr_safe(do_action_free, NULL, false);
> +}
> +
> +static struct evtact_expr_class *expr_class_list[EVTACT_EXPR_TYPE_MAX] = {
> +};
> +
> +int parse_action_expr__set_class(enum evtact_expr_type type,
> +				 struct evtact_expr_class *class)
> +{
> +	if (type >= EVTACT_EXPR_TYPE_MAX) {
> +		pr_err("action expr set class ops type invalid\n");
> +		return -EINVAL;
> +	}
> +
> +	if (expr_class_list[type] != NULL) {
> +		pr_err("action expr set class ops type already exists\n");
> +		return -EEXIST;
> +	}
> +
> +	expr_class_list[type] = class;
> +	return 0;
> +}
> +
> +static int expr_set_type(struct evtact_expr *expr)
> +{
> +	u64 id;
> +	int ret;
> +	u32 type, opcode;
> +	struct evtact_expr_class *class;
> +
> +	id = expr->id;
> +	evtact_expr_id_decode(id, &type, &opcode);
> +
> +	if (type >= EVTACT_EXPR_TYPE_MAX) {
> +		pr_err("parse_action_expr type invalid: %u\n", type);
> +		return -EINVAL;
> +	}
> +
> +	class = expr_class_list[type];
> +	if (class == NULL) {
> +		pr_err("parse_action_expr class not supported: %u\n", type);
> +		return -ENOTSUP;
> +	}
> +
> +	if (class->set_ops != NULL) {
> +		ret = class->set_ops(expr, opcode);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +struct evtact_expr *parse_action_expr__new(u64 id, struct list_head *opnds,
> +					   void *data, int size)
> +{
> +	int ret;
> +	struct evtact_expr *expr;
> +
> +	expr = malloc(sizeof(*expr));
> +	if (expr == NULL) {
> +		pr_err("parse_action_expr malloc failed\n");
> +		goto out_free_opnds;
> +	}
> +	expr->id = id;
> +
> +	if (opnds != NULL)
> +		list_add_tail(&expr->opnds, opnds);
> +	else
> +		INIT_LIST_HEAD(&expr->opnds);
> +
> +	ret = expr_set_type(expr);
> +	if (ret)
> +		goto out_list_del_opnds;
> +
> +	if (expr->ops->new != NULL) {
> +		ret = expr->ops->new(expr, data, size);
> +		if (ret)
> +			goto out_free_expr;
> +	}
> +
> +	return expr;
> +
> +out_free_expr:
> +	free(expr);
> +out_list_del_opnds:
> +	list_del(&expr->opnds);
> +out_free_opnds:
> +	parse_action_expr__free_opnds(opnds);
> +
> +	return NULL;
> +}
> +
> +void parse_action_expr__free_opnds(struct list_head *opnds)
> +{
> +	struct evtact_expr *opnd, *tmp;
> +
> +	if (opnds != NULL && !list_empty(opnds)) {
> +		list_for_each_entry_safe(opnd, tmp, opnds, list) {
> +			list_del(&opnd->list);
> +			parse_action_expr__free(opnd);
> +		}
> +	}
> +}
> +
> +void parse_action_expr__free(struct evtact_expr *expr)
> +{
> +	if (expr == NULL)
> +		return;
> +
> +	if (expr->ops->free != NULL)
> +		expr->ops->free(expr);
> +
> +	parse_action_expr__free_opnds(&expr->opnds);
> +	free(expr);
> +}
> diff --git a/tools/perf/util/parse-action.h b/tools/perf/util/parse-action.h
> new file mode 100644
> index 000000000000..71a0a166959e
> --- /dev/null
> +++ b/tools/perf/util/parse-action.h
> @@ -0,0 +1,75 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef __PERF_UTIL_PARSE_ACTION_H_
> +#define __PERF_UTIL_PARSE_ACTION_H_
> +
> +#include <linux/types.h>
> +
> +#include <subcmd/parse-options.h>
> +
> +#include "evlist.h"
> +
> +enum evtact_expr_type {
> +	EVTACT_EXPR_TYPE_MAX,
> +};
> +
> +struct evtact_expr;
> +struct evtact_expr_ops {
> +	int (*new)(struct evtact_expr *expr, void *data, int size);
> +	int (*eval)(struct evtact_expr *expr,
> +		    void *in, int in_size, void **out, int *out_size);
> +	void (*free)(struct evtact_expr *expr);
> +};
> +
> +struct evtact_expr_class {
> +	int (*set_ops)(struct evtact_expr *expr, u32 opcode);
> +};
> +
> +struct evtact_expr {
> +	struct list_head list;
> +	u64 id;
> +	struct evtact_expr_ops *ops;
> +	struct list_head opnds;
> +	void *priv;
> +};
> +
> +/*
> + * The expr id contains two fileds:
> + * |--------------|----------------|
> + * |     type     |     opcode     |
> + * |--------------|----------------|
> + *      32-bit           32-bit
> + */
> +#define EVTACT_EXPR_ID_TYPE_BITS_SHIFT 32
> +static inline u64 evtact_expr_id_encode(u32 type, u32 opcode)
> +{
> +	return (u64)type << EVTACT_EXPR_ID_TYPE_BITS_SHIFT | opcode;
> +}
> +
> +static inline void evtact_expr_id_decode(u64 id, u32 *type, u32 *opcode)
> +{
> +	if (type != NULL)
> +		*type = id >> EVTACT_EXPR_ID_TYPE_BITS_SHIFT;
> +
> +	if (opcode != NULL)
> +		*opcode = id & GENMASK(EVTACT_EXPR_ID_TYPE_BITS_SHIFT, 0);
> +}
> +
> +int parse_record_action(struct evlist *evlist, const char *str);
> +void event_actions__free(void);
> +
> +int event_actions__for_each_expr(int (*func)(struct evtact_expr *, void *arg),
> +				 void *arg, bool recursive);
> +
> +int event_actions__for_each_expr_safe(int (*func)(struct evtact_expr *, void *arg),
> +				      void *arg, bool recursive);
> +
> +struct evtact_expr *parse_action_expr__new(u64 id, struct list_head *opnds,
> +					   void *data, int size);
> +
> +void parse_action_expr__free_opnds(struct list_head *opnds);
> +void parse_action_expr__free(struct evtact_expr *expr);
> +
> +int parse_action_expr__set_class(enum evtact_expr_type type,
> +				 struct evtact_expr_class *ops);
> +
> +#endif /* __PERF_UTIL_PARSE_ACTION_H_ */
> diff --git a/tools/perf/util/parse-action.l b/tools/perf/util/parse-action.l
> new file mode 100644
> index 000000000000..3cb72de50372
> --- /dev/null
> +++ b/tools/perf/util/parse-action.l
> @@ -0,0 +1,40 @@
> +%option prefix="parse_action_"
> +%option noyywrap
> +%option stack
> +
> +%{
> +
> +#include <errno.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +
> +#include "util/debug.h"
> +
> +#include "parse-action.h"
> +#include "parse-action-bison.h"
> +
> +%}
> +
> +space		[ \t]
> +ident		[_a-zA-Z][_a-zA-Z0-9]*
> +
> +%%
> +
> +{space}		{ }
> +
> +";"		{ return SEMI; }
> +
> +{ident}		{
> +			parse_action_lval.str = strdup(parse_action_text);
> +			if (parse_action_lval.str == NULL) {
> +				pr_err("parse_action malloc ident string failed\n");
> +				return ERROR;
> +			}
> +			return IDENT;
> +		}
> +.		{
> +			pr_err("invalid character: '%s'\n", parse_action_text);
> +			return ERROR;
> +		}
> +
> +%%
> diff --git a/tools/perf/util/parse-action.y b/tools/perf/util/parse-action.y
> new file mode 100644
> index 000000000000..fade9d093d4a
> --- /dev/null
> +++ b/tools/perf/util/parse-action.y
> @@ -0,0 +1,82 @@
> +%parse-param {struct list_head *actions_head}
> +%define parse.error verbose
> +
> +%{
> +
> +#ifndef NDEBUG
> +#define YYDEBUG 1
> +#endif
> +
> +#include <errno.h>
> +#include <stdio.h>
> +#include <string.h>
> +
> +#include <linux/compiler.h>
> +#include <linux/list.h>
> +
> +#include "util/debug.h"
> +#include "util/parse-action.h"
> +
> +int parse_action_lex(void);
> +
> +static void parse_action_error(struct list_head *expr __maybe_unused,
> +			       char const *msg)
> +{
> +	pr_err("parse_action: %s\n", msg);
> +}
> +
> +%}
> +
> +%union
> +{
> +	char *str;
> +	struct evtact_expr *expr;
> +	struct list_head *list;
> +}
> +
> +%token IDENT ERROR
> +%token SEMI
> +%type <expr> action_term expr_term
> +%destructor { parse_action_expr__free($$); } <expr>
> +%type <str> IDENT
> +
> +%%
> +
> +actions:
> +action_term SEMI actions
> +{
> +	list_add(&$1->list, actions_head);
> +}
> +|
> +action_term SEMI
> +{
> +	list_add(&$1->list, actions_head);
> +}
> +|
> +action_term
> +{
> +	list_add(&$1->list, actions_head);
> +}
> +
> +action_term:
> +expr_term
> +{
> +	$$ = $1;
> +}
> +
> +expr_term:
> +IDENT
> +{
> +	$$ = NULL;
> +	pr_err("unsupported ident: '%s'\n", $1);
> +	free($1);
> +	YYERROR;
> +}
> +|
> +ERROR
> +{
> +	$$ = NULL;
> +	YYERROR;
> +}
> +
> +%%
> diff --git a/tools/perf/util/record_action.c b/tools/perf/util/record_action.c
> new file mode 100644
> index 000000000000..44789e0d4678
> --- /dev/null
> +++ b/tools/perf/util/record_action.c
> @@ -0,0 +1,15 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/**
> + * Read event sample data and execute the specified actions.
> + */
> +
> +#include "util/debug.h"
> +#include "util/parse-action.h"
> +#include "util/record_action.h"
> +
> +int bpf_perf_record(struct evlist *evlist __maybe_unused,
> +		    int argc __maybe_unused, const char **argv __maybe_unused)
> +{
> +	event_actions__free();
> +	return 0;
> +}
> diff --git a/tools/perf/util/record_action.h b/tools/perf/util/record_action.h
> new file mode 100644
> index 000000000000..289be4befa97
> --- /dev/null
> +++ b/tools/perf/util/record_action.h
> @@ -0,0 +1,24 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef __PERF_UTIL_RECORD_ACTION_H_
> +#define __PERF_UTIL_RECORD_ACTION_H_
> +
> +#include <errno.h>
> +#include "evlist.h"
> +
> +#ifdef HAVE_BPF_SKEL
> +
> +int bpf_perf_record(struct evlist *evlist, int argc, const char **argv);
> +
> +
> +#else /* !HAVE_BPF_SKEL */
> +
> +static inline int bpf_perf_record(struct evlist *evlist __maybe_unused,
> +				  int argc __maybe_unused,
> +				  const char **argv __maybe_unused)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +#endif /* !HAVE_BPF_SKEL */
> +
> +#endif /* __PERF_UTIL_RECORD_ACTION_H_ */
> -- 
> 2.25.1

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [RFC 02/12] perf event action: Add parsing const expr support
  2024-11-28 13:35 ` [RFC 02/12] perf event action: Add parsing const expr support Yang Jihong
@ 2024-11-28 20:23   ` Arnaldo Carvalho de Melo
  2024-12-04  8:29     ` [External] " Yang Jihong
  0 siblings, 1 reply; 27+ messages in thread
From: Arnaldo Carvalho de Melo @ 2024-11-28 20:23 UTC (permalink / raw)
  To: Yang Jihong
  Cc: peterz, mingo, namhyung, mark.rutland, alexander.shishkin, jolsa,
	irogers, adrian.hunter, kan.liang, james.clark, linux-perf-users,
	linux-kernel

On Thu, Nov 28, 2024 at 09:35:43PM +0800, Yang Jihong wrote:
> Event action requires constant expression parsing support,
> which include constant integer and constant string.
> 
> Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
> ---
>  tools/perf/util/parse-action.c | 27 +++++++++++++++++++++++++++
>  tools/perf/util/parse-action.h |  5 +++++
>  2 files changed, 32 insertions(+)
> 
> diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
> index 01c8c7fdea59..391546bf3d73 100644
> --- a/tools/perf/util/parse-action.c
> +++ b/tools/perf/util/parse-action.c
> @@ -4,6 +4,9 @@
>   * Actions are the programs that run when the sampling event is triggered.
>   * The action is a list of expressions separated by semicolons (;).
>   * Each action is an expression, added to actions_head node as list_head node.
> + *
> + * Supported expressions:
> + *   - constant:

This seems incomplete, what should come after the :?

the patch description, at the beginning of this message has more details
than here.

>   */
>  
>  #include "util/debug.h"
> @@ -115,7 +118,31 @@ void event_actions__free(void)
>  	(void)event_actions__for_each_expr_safe(do_action_free, NULL, false);
>  }
>  
> +static struct evtact_expr_ops *expr_const_ops_list[EVTACT_EXPR_CONST_TYPE_MAX] = {
> +};
> +
> +static int expr_const_set_ops(struct evtact_expr *expr, u32 opcode)
> +{
> +	if (opcode >= EVTACT_EXPR_CONST_TYPE_MAX) {
> +		pr_err("expr_const opcode invalid: %u\n", opcode);
> +		return -EINVAL;
> +	}
> +
> +	if (expr_const_ops_list[opcode] == NULL) {
> +		pr_err("expr_const opcode not supported: %u\n", opcode);
> +		return -ENOTSUP;
> +	}

Since expr_const_ops_list[EVTACT_EXPR_TYPE_CONST] is NULL, this will
always fail?

> +
> +	expr->ops = expr_const_ops_list[opcode];
> +	return 0;
> +}
> +
> +static struct evtact_expr_class expr_const = {
> +	.set_ops = expr_const_set_ops,
> +};
> +
>  static struct evtact_expr_class *expr_class_list[EVTACT_EXPR_TYPE_MAX] = {
> +	[EVTACT_EXPR_TYPE_CONST]   = &expr_const,
>  };
>  
>  int parse_action_expr__set_class(enum evtact_expr_type type,
> diff --git a/tools/perf/util/parse-action.h b/tools/perf/util/parse-action.h
> index 71a0a166959e..47bd75264dee 100644
> --- a/tools/perf/util/parse-action.h
> +++ b/tools/perf/util/parse-action.h
> @@ -9,9 +9,14 @@
>  #include "evlist.h"
>  
>  enum evtact_expr_type {
> +	EVTACT_EXPR_TYPE_CONST,
>  	EVTACT_EXPR_TYPE_MAX,
>  };
>  
> +enum evtact_expr_const_type {
> +	EVTACT_EXPR_CONST_TYPE_MAX,
> +};
> +
>  struct evtact_expr;
>  struct evtact_expr_ops {
>  	int (*new)(struct evtact_expr *expr, void *data, int size);
> -- 
> 2.25.1

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [RFC 03/12] perf event action: Add parsing const integer expr support
  2024-11-28 13:35 ` [RFC 03/12] perf event action: Add parsing const integer " Yang Jihong
@ 2024-11-28 20:25   ` Arnaldo Carvalho de Melo
  2024-12-04  8:32     ` [External] " Yang Jihong
  0 siblings, 1 reply; 27+ messages in thread
From: Arnaldo Carvalho de Melo @ 2024-11-28 20:25 UTC (permalink / raw)
  To: Yang Jihong
  Cc: peterz, mingo, namhyung, mark.rutland, alexander.shishkin, jolsa,
	irogers, adrian.hunter, kan.liang, james.clark, linux-perf-users,
	linux-kernel

On Thu, Nov 28, 2024 at 09:35:44PM +0800, Yang Jihong wrote:
> Support parsing of constant integer expression.
> 
> Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
> ---
>  tools/perf/util/parse-action.c | 52 ++++++++++++++++++++++++++++++++++
>  tools/perf/util/parse-action.h |  1 +
>  tools/perf/util/parse-action.l | 19 +++++++++++++
>  tools/perf/util/parse-action.y | 13 ++++++++-
>  4 files changed, 84 insertions(+), 1 deletion(-)
> 
> diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
> index 391546bf3d73..3b10cf9f99b3 100644
> --- a/tools/perf/util/parse-action.c
> +++ b/tools/perf/util/parse-action.c
> @@ -7,6 +7,7 @@
>   *
>   * Supported expressions:
>   *   - constant:
> + *     - integer

And now there are alignment differences and no : ?

>   */
>  
>  #include "util/debug.h"
> @@ -118,7 +119,58 @@ void event_actions__free(void)
>  	(void)event_actions__for_each_expr_safe(do_action_free, NULL, false);
>  }
>  
> +static int expr_const_int_new(struct evtact_expr *expr, void *data, int size)
> +{
> +	if (data == NULL ||
> +	    (size != sizeof(int)
> +	     && size != sizeof(long) && size != sizeof(long long))) {

&& should be at the end of the previous line, just like you did with the
|| at the end of the first line

> +		pr_err("expr_const_int size invalid: %d\n", size);
> +		return -EINVAL;
> +	}
> +
> +	expr->priv = malloc(sizeof(long long));
> +	if (expr->priv == NULL) {
> +		pr_err("exp_ const_int malloc failed\n");
> +		return -ENOMEM;
> +	}
> +
> +	if (size == sizeof(int))
> +		*(unsigned long long *)(expr->priv) = *(unsigned int *)data;
> +	else if (size == sizeof(long))
> +		*(unsigned long long *)(expr->priv) = *(unsigned long *)data;
> +	else if (size == sizeof(long long))
> +		*(unsigned long long *)(expr->priv) = *(unsigned long long *)data;
> +
> +	INIT_LIST_HEAD(&expr->opnds);
> +	return 0;
> +}
> +
> +static void expr_const_int_free(struct evtact_expr *expr)
> +{
> +	zfree(&expr->priv);
> +}
> +
> +static int expr_const_int_eval(struct evtact_expr *expr,
> +			       void *in __maybe_unused, int in_size __maybe_unused,
> +			       void **out, int *out_size)
> +{
> +	if (out != NULL)
> +		*out = expr->priv;
> +
> +	if (out_size != NULL)
> +		*out_size = sizeof(long long);
> +
> +	return 0;
> +}
> +
> +static struct evtact_expr_ops expr_const_int_ops = {
> +	.new  = expr_const_int_new,
> +	.free = expr_const_int_free,
> +	.eval = expr_const_int_eval,
> +};
> +
>  static struct evtact_expr_ops *expr_const_ops_list[EVTACT_EXPR_CONST_TYPE_MAX] = {
> +	[EVTACT_EXPR_CONST_TYPE_INT] = &expr_const_int_ops,
>  };
>  
>  static int expr_const_set_ops(struct evtact_expr *expr, u32 opcode)
> diff --git a/tools/perf/util/parse-action.h b/tools/perf/util/parse-action.h
> index 47bd75264dee..ac81278c590e 100644
> --- a/tools/perf/util/parse-action.h
> +++ b/tools/perf/util/parse-action.h
> @@ -14,6 +14,7 @@ enum evtact_expr_type {
>  };
>  
>  enum evtact_expr_const_type {
> +	EVTACT_EXPR_CONST_TYPE_INT,
>  	EVTACT_EXPR_CONST_TYPE_MAX,
>  };
>  
> diff --git a/tools/perf/util/parse-action.l b/tools/perf/util/parse-action.l
> index 3cb72de50372..9237399a11ac 100644
> --- a/tools/perf/util/parse-action.l
> +++ b/tools/perf/util/parse-action.l
> @@ -13,13 +13,32 @@
>  #include "parse-action.h"
>  #include "parse-action-bison.h"
>  
> +static int value(int base)
> +{
> +	unsigned long long num;
> +
> +	errno = 0;
> +	num = strtoul(parse_action_text, NULL, base);
> +	if (errno) {
> +		pr_err("parse_action malloc number failed\n");
> +		return ERROR;
> +	}
> +
> +	parse_action_lval.num = num;
> +	return NUMBER;
> +}
> +
>  %}
>  
> +num_dec		[0-9]+
> +num_hex		0[xX][0-9a-fA-F]+
>  space		[ \t]
>  ident		[_a-zA-Z][_a-zA-Z0-9]*
>  
>  %%
>  
> +{num_dec}	{ return value(10); }
> +{num_hex}	{ return value(16); }
>  {space}		{ }
>  
>  ";"		{ return SEMI; }
> diff --git a/tools/perf/util/parse-action.y b/tools/perf/util/parse-action.y
> index fade9d093d4a..51e77e54f157 100644
> --- a/tools/perf/util/parse-action.y
> +++ b/tools/perf/util/parse-action.y
> @@ -17,6 +17,8 @@
>  #include "util/debug.h"
>  #include "util/parse-action.h"
>  
> +#define expr_id(t, o) evtact_expr_id_encode(EVTACT_EXPR_TYPE_##t, EVTACT_EXPR_##t##_TYPE_##o)
> +
>  int parse_action_lex(void);
>  
>  static void parse_action_error(struct list_head *expr __maybe_unused,
> @@ -32,13 +34,15 @@ static void parse_action_error(struct list_head *expr __maybe_unused,
>  	char *str;
>  	struct evtact_expr *expr;
>  	struct list_head *list;
> +	unsigned long long num;
>  }
>  
> -%token IDENT ERROR
> +%token IDENT ERROR NUMBER
>  %token SEMI
>  %type <expr> action_term expr_term
>  %destructor { parse_action_expr__free($$); } <expr>
>  %type <str> IDENT
> +%type <num> NUMBER
>  
>  %%
>  
> @@ -65,6 +69,13 @@ expr_term
>  }
>  
>  expr_term:
> +NUMBER
> +{
> +	$$ = parse_action_expr__new(expr_id(CONST, INT), NULL, (void *)&$1, sizeof($1));
> +	if ($$ == NULL)
> +		YYERROR;
> +}
> +|
>  IDENT
>  {
>  	$$ = NULL;
> -- 
> 2.25.1

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [RFC 00/12] perf record: Add event action support
  2024-11-28 20:14 ` Arnaldo Carvalho de Melo
@ 2024-12-02 21:46   ` Namhyung Kim
  2024-12-04  8:35     ` [External] " Yang Jihong
  2024-12-04  8:21   ` Yang Jihong
  1 sibling, 1 reply; 27+ messages in thread
From: Namhyung Kim @ 2024-12-02 21:46 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Yang Jihong, peterz, mingo, mark.rutland, alexander.shishkin,
	jolsa, irogers, adrian.hunter, kan.liang, james.clark,
	linux-perf-users, linux-kernel

Hello,

On Thu, Nov 28, 2024 at 05:14:53PM -0300, Arnaldo Carvalho de Melo wrote:
> On Thu, Nov 28, 2024 at 09:35:41PM +0800, Yang Jihong wrote:
> > In perf-record, when an event is triggered, default behavior is to
> > save sample data to perf.data. Sometimes, we may just want to do
> > some lightweight actions, such as printing a log.
> 
> > Based on this requirement, add the --action option to the event to
> > specify the behavior when the event occurs.
> 
> 'perf record' is centered on saving data to disk without processing
> events, while it has sideband events for some needs, like processing BPF
> related events (PERF_RECORD_BPF_EVENT to catch PERF_BPF_EVENT_PROG_LOAD
> and UNLOAD), doing things in a "live" way as your patchkit does seems
> more appropriate to do in 'perf trace' :-)

Agreed, 'perf trace' looks like a better place as you seem to target
tracepoint events mostly.

Thanks,
Namhyung


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [External] Re: [RFC 00/12] perf record: Add event action support
  2024-11-28 13:53 ` [RFC 00/12] perf record: Add event action support Adrian Hunter
@ 2024-12-04  8:07   ` Yang Jihong
  0 siblings, 0 replies; 27+ messages in thread
From: Yang Jihong @ 2024-12-04  8:07 UTC (permalink / raw)
  To: Adrian Hunter, peterz, mingo, acme, namhyung, mark.rutland,
	alexander.shishkin, jolsa, irogers, kan.liang, james.clark,
	linux-perf-users, linux-kernel

Hello,

On 11/28/24 21:53, Adrian Hunter wrote:
> On 28/11/24 15:35, Yang Jihong wrote:
>> In perf-record, when an event is triggered, default behavior is to
>> save sample data to perf.data. Sometimes, we may just want to do
>> some lightweight actions, such as printing a log.
> 
> Why not just pipe 'perf record' to 'perf script'?
> 
> # perf record -e sched:sched_switch | perf script | head
>              perf  768231 [000] 2318380.474267: sched:sched_switch: perf:768231 [120] R ==> migration/0:18 [0]
>       migration/0      18 [000] 2318380.474294: sched:sched_switch: migration/0:18 [0] S ==> swapper/0:0 [120]
>              perf  768231 [001] 2318380.474353: sched:sched_switch: perf:768231 [120] R ==> migration/1:23 [0]
>       migration/1      23 [001] 2318380.474382: sched:sched_switch: migration/1:23 [0] S ==> swapper/1:0 [120]
>              perf  768231 [002] 2318380.474477: sched:sched_switch: perf:768231 [120] R ==> migration/2:29 [0]
>       migration/2      29 [002] 2318380.474503: sched:sched_switch: migration/2:29 [0] S ==> swapper/2:0 [120]
>              perf  768231 [003] 2318380.474513: sched:sched_switch: perf:768231 [120] R ==> migration/3:35 [0]
>       migration/3      35 [003] 2318380.474523: sched:sched_switch: migration/3:35 [0] S ==> swapper/3:0 [120]
>              perf  768231 [004] 2318380.474534: sched:sched_switch: perf:768231 [120] R ==> migration/4:41 [0]
>       migration/4      41 [004] 2318380.474541: sched:sched_switch: migration/4:41 [0] S ==> swapper/4:0 [120]

pipe 'perf record' to 'perf script' has limited extensions.
We can use bpf prog to do more (such as saving user-defined data (such 
as registers, function parameters), and customizing the format of 
user-printed logs).

Now we only implement a simple print() call, and we can support more 
calls later.

Thanks,
Yang

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [External] Re: [RFC 00/12] perf record: Add event action support
  2024-11-28 20:14 ` Arnaldo Carvalho de Melo
  2024-12-02 21:46   ` Namhyung Kim
@ 2024-12-04  8:21   ` Yang Jihong
  1 sibling, 0 replies; 27+ messages in thread
From: Yang Jihong @ 2024-12-04  8:21 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: peterz, mingo, namhyung, mark.rutland, alexander.shishkin, jolsa,
	irogers, adrian.hunter, kan.liang, james.clark, linux-perf-users,
	linux-kernel

Hello,

On 11/29/24 04:14, Arnaldo Carvalho de Melo wrote:
> On Thu, Nov 28, 2024 at 09:35:41PM +0800, Yang Jihong wrote:
>> In perf-record, when an event is triggered, default behavior is to
>> save sample data to perf.data. Sometimes, we may just want to do
>> some lightweight actions, such as printing a log.
> 
>> Based on this requirement, add the --action option to the event to
>> specify the behavior when the event occurs.
> 
> 'perf record' is centered on saving data to disk without processing
> events, while it has sideband events for some needs, like processing BPF
> related events (PERF_RECORD_BPF_EVENT to catch PERF_BPF_EVENT_PROG_LOAD
> and UNLOAD), doing things in a "live" way as your patchkit does seems
> more appropriate to do in 'perf trace' :-)

Ok, we can put it in "perf trace".

> 
> I'll take a look at the rest of the patch series.

Thanks for reviewing this patchset. Currently, this patchset only 
implements a print() call and only supports attaching to tracepoints. 
The functionality is relatively simple.

Eventually, we can use bpf prog to allow users to customize the sampled 
data to and the processing of the data.

we can continue to expand the functionality (for example, attaching to 
kprobe events, printing the parameters and return values ​​of the target 
function)


Thanks,
Yang

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [External] Re: [RFC 01/12] perf record: Add event action support
  2024-11-28 20:19   ` Arnaldo Carvalho de Melo
@ 2024-12-04  8:24     ` Yang Jihong
  0 siblings, 0 replies; 27+ messages in thread
From: Yang Jihong @ 2024-12-04  8:24 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: peterz, mingo, namhyung, mark.rutland, alexander.shishkin, jolsa,
	irogers, adrian.hunter, kan.liang, james.clark, linux-perf-users,
	linux-kernel

Hello,

On 11/29/24 04:19, Arnaldo Carvalho de Melo wrote:
> On Thu, Nov 28, 2024 at 09:35:42PM +0800, Yang Jihong wrote:
>> In perf-record, when an event is triggered, default behavior is to
>> save sample data to perf.data. Sometimes, we may just want to do
>> some lightweight actions, such as printing a log.
>>
>> Based on this requirement, add the --action option to the event to
>> specify the behavior when the event occurs.
>>
>> Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
>> ---
>>   tools/perf/Documentation/perf-record.txt |   8 +
>>   tools/perf/builtin-record.c              |  31 +++
>>   tools/perf/util/Build                    |  18 ++
>>   tools/perf/util/parse-action.c           | 230 +++++++++++++++++++++++
>>   tools/perf/util/parse-action.h           |  75 ++++++++
>>   tools/perf/util/parse-action.l           |  40 ++++
>>   tools/perf/util/parse-action.y           |  82 ++++++++
>>   tools/perf/util/record_action.c          |  15 ++
>>   tools/perf/util/record_action.h          |  24 +++
>>   9 files changed, 523 insertions(+)
>>   create mode 100644 tools/perf/util/parse-action.c
>>   create mode 100644 tools/perf/util/parse-action.h
>>   create mode 100644 tools/perf/util/parse-action.l
>>   create mode 100644 tools/perf/util/parse-action.y
>>   create mode 100644 tools/perf/util/record_action.c
>>   create mode 100644 tools/perf/util/record_action.h
>>
>> diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
>> index 242223240a08..d0d9e0f69f3d 100644
>> --- a/tools/perf/Documentation/perf-record.txt
>> +++ b/tools/perf/Documentation/perf-record.txt
>> @@ -833,6 +833,14 @@ filtered through the mask provided by -C option.
>>   	Prepare BPF filter to be used by regular users.  The action should be
>>   	either "pin" or "unpin".  The filter can be used after it's pinned.
>>   
>> +--action=<action>::
>> +	Actions are the programs that run when the sampling event is triggered.
>> +	The action is a list of expressions separated by semicolons (;).
>> +	The sample data is saved by bpf prog attached by the event.
>> +	The call currently supported is print(); some commonly used built-in special
>> +	variables are also supported
>> +	For example:
>> +	  # perf record -e sched:sched_switch --action 'print("[%llu]comm=%s, cpu=%d, pid=%d, tid=%d\n", time, comm, cpu, pid, tid)' true
> 
> But at this point in the series this isn't available, right?
> 
> I.e. when testing this specific patch I can't follow what the
> documentation above says and expect anything, right? It will just fail?

Yes, The entire patchset needs to be applied to fully support this feature.

This patch simply implements the data organization structure.

Thanks,
Yang


^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [External] Re: [RFC 02/12] perf event action: Add parsing const expr support
  2024-11-28 20:23   ` Arnaldo Carvalho de Melo
@ 2024-12-04  8:29     ` Yang Jihong
  0 siblings, 0 replies; 27+ messages in thread
From: Yang Jihong @ 2024-12-04  8:29 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: peterz, mingo, namhyung, mark.rutland, alexander.shishkin, jolsa,
	irogers, adrian.hunter, kan.liang, james.clark, linux-perf-users,
	linux-kernel

Hello,

On 11/29/24 04:23, Arnaldo Carvalho de Melo wrote:
> On Thu, Nov 28, 2024 at 09:35:43PM +0800, Yang Jihong wrote:
>> Event action requires constant expression parsing support,
>> which include constant integer and constant string.
>>
>> Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
>> ---
>>   tools/perf/util/parse-action.c | 27 +++++++++++++++++++++++++++
>>   tools/perf/util/parse-action.h |  5 +++++
>>   2 files changed, 32 insertions(+)
>>
>> diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
>> index 01c8c7fdea59..391546bf3d73 100644
>> --- a/tools/perf/util/parse-action.c
>> +++ b/tools/perf/util/parse-action.c
>> @@ -4,6 +4,9 @@
>>    * Actions are the programs that run when the sampling event is triggered.
>>    * The action is a list of expressions separated by semicolons (;).
>>    * Each action is an expression, added to actions_head node as list_head node.
>> + *
>> + * Supported expressions:
>> + *   - constant:
> 
> This seems incomplete, what should come after the :?
> 
> the patch description, at the beginning of this message has more details
> than here.
This patch only implements a general constant expression category. The 
next patch will support parsing of specific string constant expressions 
and integer constant expressions.


> 
>>    */
>>   
>>   #include "util/debug.h"
>> @@ -115,7 +118,31 @@ void event_actions__free(void)
>>   	(void)event_actions__for_each_expr_safe(do_action_free, NULL, false);
>>   }
>>   
>> +static struct evtact_expr_ops *expr_const_ops_list[EVTACT_EXPR_CONST_TYPE_MAX] = {
>> +};
>> +
>> +static int expr_const_set_ops(struct evtact_expr *expr, u32 opcode)
>> +{
>> +	if (opcode >= EVTACT_EXPR_CONST_TYPE_MAX) {
>> +		pr_err("expr_const opcode invalid: %u\n", opcode);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (expr_const_ops_list[opcode] == NULL) {
>> +		pr_err("expr_const opcode not supported: %u\n", opcode);
>> +		return -ENOTSUP;
>> +	}
> 
> Since expr_const_ops_list[EVTACT_EXPR_TYPE_CONST] is NULL, this will
> always fail?
> 
Yes, this patch does not support specific constant expressions, so it is 
empty.

Thanks,
Yang.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [External] Re: [RFC 03/12] perf event action: Add parsing const integer expr support
  2024-11-28 20:25   ` Arnaldo Carvalho de Melo
@ 2024-12-04  8:32     ` Yang Jihong
  0 siblings, 0 replies; 27+ messages in thread
From: Yang Jihong @ 2024-12-04  8:32 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: peterz, mingo, namhyung, mark.rutland, alexander.shishkin, jolsa,
	irogers, adrian.hunter, kan.liang, james.clark, linux-perf-users,
	linux-kernel

Hello,

On 11/29/24 04:25, Arnaldo Carvalho de Melo wrote:
> On Thu, Nov 28, 2024 at 09:35:44PM +0800, Yang Jihong wrote:
>> Support parsing of constant integer expression.
>>
>> Signed-off-by: Yang Jihong <yangjihong@bytedance.com>
>> ---
>>   tools/perf/util/parse-action.c | 52 ++++++++++++++++++++++++++++++++++
>>   tools/perf/util/parse-action.h |  1 +
>>   tools/perf/util/parse-action.l | 19 +++++++++++++
>>   tools/perf/util/parse-action.y | 13 ++++++++-
>>   4 files changed, 84 insertions(+), 1 deletion(-)
>>
>> diff --git a/tools/perf/util/parse-action.c b/tools/perf/util/parse-action.c
>> index 391546bf3d73..3b10cf9f99b3 100644
>> --- a/tools/perf/util/parse-action.c
>> +++ b/tools/perf/util/parse-action.c
>> @@ -7,6 +7,7 @@
>>    *
>>    * Supported expressions:
>>    *   - constant:
>> + *     - integer
> 
> And now there are alignment differences and no : ?
This indicates that integer expression is a subclass of constant 
expression, so integer is indented inside constant.
> 
>>    */
>>   
>>   #include "util/debug.h"
>> @@ -118,7 +119,58 @@ void event_actions__free(void)
>>   	(void)event_actions__for_each_expr_safe(do_action_free, NULL, false);
>>   }
>>   
>> +static int expr_const_int_new(struct evtact_expr *expr, void *data, int size)
>> +{
>> +	if (data == NULL ||
>> +	    (size != sizeof(int)
>> +	     && size != sizeof(long) && size != sizeof(long long))) {
> 
> && should be at the end of the previous line, just like you did with the
> || at the end of the first line
Okay, will fix in next version.

Thanks,
Yang

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [External] Re: [RFC 00/12] perf record: Add event action support
  2024-12-02 21:46   ` Namhyung Kim
@ 2024-12-04  8:35     ` Yang Jihong
  2024-12-04 19:54       ` Ian Rogers
  0 siblings, 1 reply; 27+ messages in thread
From: Yang Jihong @ 2024-12-04  8:35 UTC (permalink / raw)
  To: Namhyung Kim, Arnaldo Carvalho de Melo
  Cc: peterz, mingo, mark.rutland, alexander.shishkin, jolsa, irogers,
	adrian.hunter, kan.liang, james.clark, linux-perf-users,
	linux-kernel

Hello,

On 12/3/24 05:46, Namhyung Kim wrote:
> Hello,
> 
> On Thu, Nov 28, 2024 at 05:14:53PM -0300, Arnaldo Carvalho de Melo wrote:
>> On Thu, Nov 28, 2024 at 09:35:41PM +0800, Yang Jihong wrote:
>>> In perf-record, when an event is triggered, default behavior is to
>>> save sample data to perf.data. Sometimes, we may just want to do
>>> some lightweight actions, such as printing a log.
>>
>>> Based on this requirement, add the --action option to the event to
>>> specify the behavior when the event occurs.
>>
>> 'perf record' is centered on saving data to disk without processing
>> events, while it has sideband events for some needs, like processing BPF
>> related events (PERF_RECORD_BPF_EVENT to catch PERF_BPF_EVENT_PROG_LOAD
>> and UNLOAD), doing things in a "live" way as your patchkit does seems
>> more appropriate to do in 'perf trace' :-)
> 
> Agreed, 'perf trace' looks like a better place as you seem to target
> tracepoint events mostly.
> 
Okay, will do it in 'perf trace'.

Attaching to a kprobe events will also be supported in the future.

Thanks,
Yang

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [External] Re: [RFC 00/12] perf record: Add event action support
  2024-12-04  8:35     ` [External] " Yang Jihong
@ 2024-12-04 19:54       ` Ian Rogers
  2024-12-05 14:01         ` Yang Jihong
  0 siblings, 1 reply; 27+ messages in thread
From: Ian Rogers @ 2024-12-04 19:54 UTC (permalink / raw)
  To: Yang Jihong
  Cc: Namhyung Kim, Arnaldo Carvalho de Melo, peterz, mingo,
	mark.rutland, alexander.shishkin, jolsa, adrian.hunter, kan.liang,
	james.clark, linux-perf-users, linux-kernel

On Wed, Dec 4, 2024 at 12:35 AM Yang Jihong <yangjihong@bytedance.com> wrote:
>
> Hello,
>
> On 12/3/24 05:46, Namhyung Kim wrote:
> > Hello,
> >
> > On Thu, Nov 28, 2024 at 05:14:53PM -0300, Arnaldo Carvalho de Melo wrote:
> >> On Thu, Nov 28, 2024 at 09:35:41PM +0800, Yang Jihong wrote:
> >>> In perf-record, when an event is triggered, default behavior is to
> >>> save sample data to perf.data. Sometimes, we may just want to do
> >>> some lightweight actions, such as printing a log.
> >>
> >>> Based on this requirement, add the --action option to the event to
> >>> specify the behavior when the event occurs.
> >>
> >> 'perf record' is centered on saving data to disk without processing
> >> events, while it has sideband events for some needs, like processing BPF
> >> related events (PERF_RECORD_BPF_EVENT to catch PERF_BPF_EVENT_PROG_LOAD
> >> and UNLOAD), doing things in a "live" way as your patchkit does seems
> >> more appropriate to do in 'perf trace' :-)
> >
> > Agreed, 'perf trace' looks like a better place as you seem to target
> > tracepoint events mostly.
> >
> Okay, will do it in 'perf trace'.
>
> Attaching to a kprobe events will also be supported in the future.

Hi Yang,

Just some extra information in case it is useful on python scripting,
which could be reused to avoid writing a new interpreter as in this
series. There are quite a few perf script examples here:
https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/scripts/python?h=perf-tools-next
There are also standalone scripts here including for tracepoints:
https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/python?h=perf-tools-next
One of the things missing for standalone scripts was being able to
conveniently parse events - you'd need to manually determine PMU type
and config values. That is fixed in this reviewed but not merged
series:
https://lore.kernel.org/lkml/20241119011644.971342-1-irogers@google.com/

I'd like to see the python code improved as it allows quicker command
creation like your example. Another example I'd recently tweaked is:
https://lore.kernel.org/lkml/20241119180130.19160-1-irogers@google.com/
Ultimately I think it would be nice to be creating tools using UIs
like textualize:
https://www.textualize.io/
and our Gecko Google summer-of-code project was an improvement here:
https://lore.kernel.org/lkml/ZOrsiZA+C0zbWEQS@yoga/

Other than basic functionality, perf's python support could be
improved in a number of ways that we've discussed in office hours or
on the list. Some things that come to mind:
1) The perf script callback mechanism doesn't feel particularly
pythonic, generators or coroutines could be better.
2) We should probably have more library python code rather than adding
new functions to `tools/perf/util/python.c` - I may do this to add
type hints for mypy. If we could have a subset that works without the
C code then it could be a basis for `perf.data` file analysis not tied
to Linux, or `pip install`-able. There is a similar library with
simpleperf, but shipping something with the perf codebase means we can
keep the file format and library synchronized.
3) We'd like to reduce the number of dependencies `perf` as a command
has, and libpython is a big one. Something `uftrace` did was dlopen
libpython to avoid needing to link against it. A project similarly
rethinking python binding with C code is HPy.

Thanks,
Ian

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [External] Re: [RFC 00/12] perf record: Add event action support
  2024-12-04 19:54       ` Ian Rogers
@ 2024-12-05 14:01         ` Yang Jihong
  0 siblings, 0 replies; 27+ messages in thread
From: Yang Jihong @ 2024-12-05 14:01 UTC (permalink / raw)
  To: Ian Rogers, Namhyung Kim, Arnaldo Carvalho de Melo
  Cc: peterz, mingo, mark.rutland, alexander.shishkin, jolsa,
	adrian.hunter, kan.liang, james.clark, linux-perf-users,
	linux-kernel

Hello,

On 12/5/24 03:54, Ian Rogers wrote:
> On Wed, Dec 4, 2024 at 12:35 AM Yang Jihong <yangjihong@bytedance.com> wrote:
>>
>> Hello,
>>
>> On 12/3/24 05:46, Namhyung Kim wrote:
>>> Hello,
>>>
>>> On Thu, Nov 28, 2024 at 05:14:53PM -0300, Arnaldo Carvalho de Melo wrote:
>>>> On Thu, Nov 28, 2024 at 09:35:41PM +0800, Yang Jihong wrote:
>>>>> In perf-record, when an event is triggered, default behavior is to
>>>>> save sample data to perf.data. Sometimes, we may just want to do
>>>>> some lightweight actions, such as printing a log.
>>>>
>>>>> Based on this requirement, add the --action option to the event to
>>>>> specify the behavior when the event occurs.
>>>>
>>>> 'perf record' is centered on saving data to disk without processing
>>>> events, while it has sideband events for some needs, like processing BPF
>>>> related events (PERF_RECORD_BPF_EVENT to catch PERF_BPF_EVENT_PROG_LOAD
>>>> and UNLOAD), doing things in a "live" way as your patchkit does seems
>>>> more appropriate to do in 'perf trace' :-)
>>>
>>> Agreed, 'perf trace' looks like a better place as you seem to target
>>> tracepoint events mostly.
>>>
>> Okay, will do it in 'perf trace'.
>>
>> Attaching to a kprobe events will also be supported in the future.
> 
> Hi Yang,
> 
> Just some extra information in case it is useful on python scripting,
> which could be reused to avoid writing a new interpreter as in this
> series. There are quite a few perf script examples here:

Thanks for your suggestion. I think using python scripting, similar to 
bcc, is more suitable for scenarios with complex requirements.
For some simple and lightweight scenarios, such as just printing some 
information in real time, one-line command (similar to dtrace)may be 
more suitable to implement user-defined sample.
This is just my idea, hope to discuss to improve the usability of 
perf-tool. :-)

> https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/scripts/python?h=perf-tools-next
> There are also standalone scripts here including for tracepoints:
> https://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git/tree/tools/perf/python?h=perf-tools-next
> One of the things missing for standalone scripts was being able to
> conveniently parse events - you'd need to manually determine PMU type
> and config values. That is fixed in this reviewed but not merged
> series:
> https://lore.kernel.org/lkml/20241119011644.971342-1-irogers@google.com/
> 
> I'd like to see the python code improved as it allows quicker command
> creation like your example. Another example I'd recently tweaked is:
> https://lore.kernel.org/lkml/20241119180130.19160-1-irogers@google.com/
> Ultimately I think it would be nice to be creating tools using UIs
> like textualize:
> https://www.textualize.io/
> and our Gecko Google summer-of-code project was an improvement here:
> https://lore.kernel.org/lkml/ZOrsiZA+C0zbWEQS@yoga/
> 
> Other than basic functionality, perf's python support could be
> improved in a number of ways that we've discussed in office hours or
> on the list. Some things that come to mind:
> 1) The perf script callback mechanism doesn't feel particularly
> pythonic, generators or coroutines could be better.
> 2) We should probably have more library python code rather than adding
> new functions to `tools/perf/util/python.c` - I may do this to add
> type hints for mypy. If we could have a subset that works without the
> C code then it could be a basis for `perf.data` file analysis not tied
> to Linux, or `pip install`-able. There is a similar library with
> simpleperf, but shipping something with the perf codebase means we can
> keep the file format and library synchronized.
> 3) We'd like to reduce the number of dependencies `perf` as a command
> has, and libpython is a big one. Something `uftrace` did was dlopen
> libpython to avoid needing to link against it. A project similarly
> rethinking python binding with C code is HPy.
> 
Yes, we can greatly simplify the development work by using python.
In the end, if we can allow users to import perf python library and use 
a few simple codes to implement a customized data collection, it will be 
of great help to improve the usability of the perf tool.


Actually, I think perf-tool has a lot of functions and is very helpful 
for performance optimization, but the threshold is relatively high for 
beginners. (I don’t know if this is appropriate :-))

I have some considerations in improving the usability of the tool, but I 
don’t know if it is appropriate:
1. Provide rich prompt information, such as perf-help can provide some 
user requirements in an interactive way, provide common perf commands 
and usage methods; or provide some usage cases through keywords
2. Use some graphical interfaces as the backend so that users can use it 
in an easy-to-understand way
3. Based on the python you mentioned above, I think it is a good way to 
allow some developers to customize sampling data more easily.


Thanks,
Yang.

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2024-12-05 14:01 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-28 13:35 [RFC 00/12] perf record: Add event action support Yang Jihong
2024-11-28 13:35 ` [RFC 01/12] " Yang Jihong
2024-11-28 20:19   ` Arnaldo Carvalho de Melo
2024-12-04  8:24     ` [External] " Yang Jihong
2024-11-28 13:35 ` [RFC 02/12] perf event action: Add parsing const expr support Yang Jihong
2024-11-28 20:23   ` Arnaldo Carvalho de Melo
2024-12-04  8:29     ` [External] " Yang Jihong
2024-11-28 13:35 ` [RFC 03/12] perf event action: Add parsing const integer " Yang Jihong
2024-11-28 20:25   ` Arnaldo Carvalho de Melo
2024-12-04  8:32     ` [External] " Yang Jihong
2024-11-28 13:35 ` [RFC 04/12] perf event action: Add parsing const string " Yang Jihong
2024-11-28 13:35 ` [RFC 05/12] perf event action: Add parsing call " Yang Jihong
2024-11-28 13:35 ` [RFC 06/12] perf event action: Add parsing print() " Yang Jihong
2024-11-28 13:35 ` [RFC 07/12] perf event action: Add parsing builtin " Yang Jihong
2024-11-28 13:35 ` [RFC 08/12] perf event action: Add parsing builtin cpu " Yang Jihong
2024-11-28 13:35 ` [RFC 09/12] perf event action: Add parsing builtin pid " Yang Jihong
2024-11-28 13:35 ` [RFC 10/12] perf event action: Add parsing builtin tid " Yang Jihong
2024-11-28 13:35 ` [RFC 11/12] perf event action: Add parsing builtin comm " Yang Jihong
2024-11-28 13:35 ` [RFC 12/12] perf event action: Add parsing builtin time " Yang Jihong
2024-11-28 13:53 ` [RFC 00/12] perf record: Add event action support Adrian Hunter
2024-12-04  8:07   ` [External] " Yang Jihong
2024-11-28 20:14 ` Arnaldo Carvalho de Melo
2024-12-02 21:46   ` Namhyung Kim
2024-12-04  8:35     ` [External] " Yang Jihong
2024-12-04 19:54       ` Ian Rogers
2024-12-05 14:01         ` Yang Jihong
2024-12-04  8:21   ` Yang Jihong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).