Messages in this thread Patch in this message |  | | From | Yang Jihong <> | | Subject | [RFC 06/12] perf event action: Add parsing print() call expr support | | Date | Thu, 28 Nov 2024 21:35:47 +0800 |
| |
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(®ex, re, REG_EXTENDED)) { + pr_err("expr call print fmt regcomp failed\n"); + return -1; + } + + s = fmt; + for (i = 0;; i++) { + if (regexec(®ex, 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
|  |