lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20241128133553.823722-7-yangjihong@bytedance.com>
Date: Thu, 28 Nov 2024 21:35:47 +0800
From: Yang Jihong <yangjihong@...edance.com>
To: peterz@...radead.org,
	mingo@...hat.com,
	acme@...nel.org,
	namhyung@...nel.org,
	mark.rutland@....com,
	alexander.shishkin@...ux.intel.com,
	jolsa@...nel.org,
	irogers@...gle.com,
	adrian.hunter@...el.com,
	kan.liang@...ux.intel.com,
	james.clark@....com,
	linux-perf-users@...r.kernel.org,
	linux-kernel@...r.kernel.org
Cc: yangjihong@...edance.com
Subject: [RFC 06/12] perf event action: Add parsing print() call expr support

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@...edance.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


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ