[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <ZyktL05NmqRTr213@x1>
Date: Mon, 4 Nov 2024 17:23:11 -0300
From: Arnaldo Carvalho de Melo <acme@...nel.org>
To: "Masami Hiramatsu (Google)" <mhiramat@...nel.org>
Cc: Namhyung Kim <namhyung@...nel.org>,
Peter Zijlstra <peterz@...radead.org>,
Ingo Molnar <mingo@...hat.com>, Ian Rogers <irogers@...gle.com>,
Dima Kogan <dima@...retsauce.net>,
Alexander Lobakin <aleksander.lobakin@...el.com>,
Przemek Kitszel <przemyslaw.kitszel@...el.com>,
linux-perf-users@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH 3/4] perf-probe: Introduce quotation marks support
On Tue, Nov 05, 2024 at 01:17:35AM +0900, Masami Hiramatsu (Google) wrote:
> From: Masami Hiramatsu (Google) <mhiramat@...nel.org>
>
> In non-C languages, it is possible to have ':' in the function names.
> It is possible to escape it with backslashes, but if there are too many
> backslashes, it is annoying.
> This introduce quotation marks (`"` or `'`) support.
Can you please split the patch so that strpbrk_esq() and strdup_esq()
are introduced in different patches?
- Arnaldo
> For example, without quotes, we have to pass it as below
>
> $ perf probe -x cro3 -L "cro3\:\:cmd\:\:servo\:\:run_show"
> <run_show@...rk/cro3/src/cmd/servo.rs:0>
> 0 fn run_show(args: &ArgsShow) -> Result<()> {
> 1 let list = ServoList::discover()?;
> 2 let s = list.find_by_serial(&args.servo)?;
> 3 if args.json {
> 4 println!("{s}");
>
> With quotes, we can more naturally write the function name as below;
>
> $ ./perf probe -x cro3 -L \"cro3::cmd::servo::run_show\"
> <run_show@...rk/cro3/src/cmd/servo.rs:0>
> 0 fn run_show(args: &ArgsShow) -> Result<()> {
> 1 let list = ServoList::discover()?;
> 2 let s = list.find_by_serial(&args.servo)?;
> 3 if args.json {
> 4 println!("{s}");
>
> Signed-off-by: Masami Hiramatsu (Google) <mhiramat@...nel.org>
> ---
> tools/perf/util/probe-event.c | 76 ++++++++++++++++--------------
> tools/perf/util/probe-finder.c | 3 +
> tools/perf/util/string.c | 100 ++++++++++++++++++++++++++++++++++++++++
> tools/perf/util/string2.h | 2 +
> 4 files changed, 145 insertions(+), 36 deletions(-)
>
> diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
> index 913a27cbb5d9..bcba8273204d 100644
> --- a/tools/perf/util/probe-event.c
> +++ b/tools/perf/util/probe-event.c
> @@ -1065,6 +1065,7 @@ static int __show_line_range(struct line_range *lr, const char *module,
>
> ret = debuginfo__find_line_range(dinfo, lr);
> if (!ret) { /* Not found, retry with an alternative */
> + pr_debug2("Failed to find line range in debuginfo. Fallback to alternative\n");
> ret = get_alternative_line_range(dinfo, lr, module, user);
> if (!ret)
> ret = debuginfo__find_line_range(dinfo, lr);
> @@ -1078,7 +1079,7 @@ static int __show_line_range(struct line_range *lr, const char *module,
> pr_warning("Specified source line is not found.\n");
> return -ENOENT;
> } else if (ret < 0) {
> - pr_warning("Debuginfo analysis failed.\n");
> + pr_warning("Debuginfo analysis failed (%d).\n", ret);
> return ret;
> }
>
> @@ -1187,7 +1188,7 @@ static int show_available_vars_at(struct debuginfo *dinfo,
> pr_err("Failed to find the address of %s\n", buf);
> ret = -ENOENT;
> } else
> - pr_warning("Debuginfo analysis failed.\n");
> + pr_warning("Debuginfo analysis failed(2).\n");
> goto end;
> }
>
> @@ -1343,30 +1344,39 @@ static bool is_c_func_name(const char *name)
> *
> * @SRC[:SLN[+NUM|-ELN]]
> * FNC[@SRC][:SLN[+NUM|-ELN]]
> + *
> + * SRC and FUNC can be quoted by double/single quotes.
> */
> int parse_line_range_desc(const char *arg, struct line_range *lr)
> {
> - char *range, *file, *name = strdup(arg);
> + char *buf = strdup(arg);
> + char *p;
> int err;
>
> - if (!name)
> + if (!buf)
> return -ENOMEM;
>
> lr->start = 0;
> lr->end = INT_MAX;
>
> - range = strpbrk_esc(name, ":");
> - if (range) {
> - *range++ = '\0';
> + pr_debug2("Input line range: %s\n", buf);
> + p = strpbrk_esq(buf, ":");
> + if (p) {
> + if (p == buf) {
> + semantic_error("No file/function name in '%s'.\n", p);
> + err = -EINVAL;
> + goto err;
> + }
> + *(p++) = '\0';
>
> - err = parse_line_num(&range, &lr->start, "start line");
> + err = parse_line_num(&p, &lr->start, "start line");
> if (err)
> goto err;
>
> - if (*range == '+' || *range == '-') {
> - const char c = *range++;
> + if (*p == '+' || *p == '-') {
> + const char c = *(p++);
>
> - err = parse_line_num(&range, &lr->end, "end line");
> + err = parse_line_num(&p, &lr->end, "end line");
> if (err)
> goto err;
>
> @@ -1390,28 +1400,22 @@ int parse_line_range_desc(const char *arg, struct line_range *lr)
> " than end line.\n");
> goto err;
> }
> - if (*range != '\0') {
> - semantic_error("Tailing with invalid str '%s'.\n", range);
> + if (*p != '\0') {
> + semantic_error("Tailing with invalid str '%s'.\n", p);
> goto err;
> }
> }
>
> - file = strpbrk_esc(name, "@");
> - if (file) {
> - *file = '\0';
> - lr->file = strdup_esc(++file);
> - if (lr->file == NULL) {
> - err = -ENOMEM;
> - goto err;
> - }
> - if (*name != '\0')
> - lr->function = name;
> - } else
> - lr->function = name;
> + p = strpbrk_esq(buf, "@");
> + if (p) {
> + *(p++) = '\0';
> + lr->file = strdup_esq(p);
> + }
> + lr->function = strdup_esq(buf);
> + err = 0;
>
> - return 0;
> err:
> - free(name);
> + free(buf);
> return err;
> }
>
> @@ -1419,19 +1423,19 @@ static int parse_perf_probe_event_name(char **arg, struct perf_probe_event *pev)
> {
> char *ptr;
>
> - ptr = strpbrk_esc(*arg, ":");
> + ptr = strpbrk_esq(*arg, ":");
> if (ptr) {
> *ptr = '\0';
> if (!pev->sdt && !is_c_func_name(*arg))
> goto ng_name;
> - pev->group = strdup_esc(*arg);
> + pev->group = strdup_esq(*arg);
> if (!pev->group)
> return -ENOMEM;
> *arg = ptr + 1;
> } else
> pev->group = NULL;
>
> - pev->event = strdup_esc(*arg);
> + pev->event = strdup_esq(*arg);
> if (pev->event == NULL)
> return -ENOMEM;
>
> @@ -1470,7 +1474,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
> arg++;
> }
>
> - ptr = strpbrk_esc(arg, ";=@+%");
> + ptr = strpbrk_esq(arg, ";=@+%");
> if (pev->sdt) {
> if (ptr) {
> if (*ptr != '@') {
> @@ -1484,7 +1488,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
> pev->target = build_id_cache__origname(tmp);
> free(tmp);
> } else
> - pev->target = strdup_esc(ptr + 1);
> + pev->target = strdup_esq(ptr + 1);
> if (!pev->target)
> return -ENOMEM;
> *ptr = '\0';
> @@ -1518,7 +1522,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
> arg++;
> }
>
> - ptr = strpbrk_esc(arg, ";:+@%");
> + ptr = strpbrk_esq(arg, ";:+@%");
> if (ptr) {
> nc = *ptr;
> *ptr++ = '\0';
> @@ -1527,7 +1531,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
> if (arg[0] == '\0')
> tmp = NULL;
> else {
> - tmp = strdup_esc(arg);
> + tmp = strdup_esq(arg);
> if (tmp == NULL)
> return -ENOMEM;
> }
> @@ -1565,7 +1569,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
> return -ENOMEM;
> break;
> }
> - ptr = strpbrk_esc(arg, ";:+@%");
> + ptr = strpbrk_esq(arg, ";:+@%");
> if (ptr) {
> nc = *ptr;
> *ptr++ = '\0';
> @@ -1592,7 +1596,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
> semantic_error("SRC@SRC is not allowed.\n");
> return -EINVAL;
> }
> - pp->file = strdup_esc(arg);
> + pp->file = strdup_esq(arg);
> if (pp->file == NULL)
> return -ENOMEM;
> break;
> diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
> index 630e16c54ed5..5462b5541a6d 100644
> --- a/tools/perf/util/probe-finder.c
> +++ b/tools/perf/util/probe-finder.c
> @@ -1796,6 +1796,7 @@ int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr)
> struct dwarf_callback_param line_range_param = {
> .data = (void *)&lf, .retval = 0};
>
> + pr_debug2("Start searching function '%s' in getpubname\n", lr->function);
> dwarf_getpubnames(dbg->dbg, pubname_search_cb,
> &pubname_param, 0);
> if (pubname_param.found) {
> @@ -1803,8 +1804,10 @@ int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr)
> if (lf.found)
> goto found;
> }
> + pr_debug2("Not found, use DIE tree\n");
> }
>
> + pr_debug2("Search function '%s' in DIE tree\n", lr->function);
> /* Loop on CUs (Compilation Unit) */
> while (!lf.found && ret >= 0) {
> if (dwarf_nextcu(dbg->dbg, off, &noff, &cuhl,
> diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
> index 116a642ad99d..308fc7ec88cc 100644
> --- a/tools/perf/util/string.c
> +++ b/tools/perf/util/string.c
> @@ -263,6 +263,34 @@ char *strpbrk_esc(char *str, const char *stopset)
> return ptr;
> }
>
> +/* Like strpbrk_esc(), but not break if it is quoted with single/double quotes */
> +char *strpbrk_esq(char *str, const char *stopset)
> +{
> + char *_stopset = NULL;
> + char *ptr;
> + const char *squote = "'";
> + const char *dquote = "\"";
> +
> + if (asprintf(&_stopset, "%s%c%c", stopset, *squote, *dquote) < 0)
> + return NULL;
> +
> + do {
> + ptr = strpbrk_esc(str, _stopset);
> + if (!ptr)
> + break;
> + if (*ptr == *squote)
> + ptr = strpbrk_esc(ptr + 1, squote);
> + else if (*ptr == *dquote)
> + ptr = strpbrk_esc(ptr + 1, dquote);
> + else
> + break;
> + str = ptr + 1;
> + } while (ptr);
> +
> + free(_stopset);
> + return ptr;
> +}
> +
> /* Like strdup, but do not copy a single backslash */
> char *strdup_esc(const char *str)
> {
> @@ -293,6 +321,78 @@ char *strdup_esc(const char *str)
> return ret;
> }
>
> +/* Remove backslash right before quote and return next quote address. */
> +static char *remove_consumed_esc(char *str, int len, int quote)
> +{
> + char *ptr = str, *end = str + len;
> +
> + while (*ptr != quote && ptr < end) {
> + if (*ptr == '\\' && *(ptr + 1) == quote) {
> + memmove(ptr, ptr + 1, end - (ptr + 1));
> + /* now *ptr is `quote`. */
> + end--;
> + }
> + ptr++;
> + }
> +
> + return *ptr == quote ? ptr : NULL;
> +}
> +
> +/*
> + * Like strdup_esc, but keep quoted string as it is (and single backslash
> + * before quote is removed). If there is no closed quote, return NULL.
> + */
> +char *strdup_esq(const char *str)
> +{
> + char *d, *ret;
> +
> + /* If there is no quote, return normal strdup_esc() */
> + d = strpbrk_esc((char *)str, "\"'");
> + if (!d)
> + return strdup_esc(str);
> +
> + ret = strdup(str);
> + if (!ret)
> + return NULL;
> +
> + d = ret;
> + do {
> + d = strpbrk(d, "\\\"\'");
> + if (!d)
> + break;
> +
> + if (*d == '"' || *d == '\'') {
> + /* This is non-escaped quote */
> + int quote = *d;
> + int len = strlen(d + 1) + 1;
> +
> + /*
> + * Remove the start quote and remove consumed escape (backslash
> + * before quote) and remove the end quote. If there is no end
> + * quote, it is the input error.
> + */
> + memmove(d, d + 1, len);
> + d = remove_consumed_esc(d, len, quote);
> + if (!d)
> + goto error;
> + memmove(d, d + 1, strlen(d + 1) + 1);
> + }
> + if (*d == '\\') {
> + memmove(d, d + 1, strlen(d + 1) + 1);
> + if (*d == '\\') {
> + /* double backslash -- keep the second one. */
> + d++;
> + }
> + }
> + } while (*d != '\0');
> +
> + return ret;
> +
> +error:
> + free(ret);
> + return NULL;
> +}
> +
> unsigned int hex(char c)
> {
> if (c >= '0' && c <= '9')
> diff --git a/tools/perf/util/string2.h b/tools/perf/util/string2.h
> index 52cb8ba057c7..4c8bff47cfd3 100644
> --- a/tools/perf/util/string2.h
> +++ b/tools/perf/util/string2.h
> @@ -37,6 +37,8 @@ char *asprintf__tp_filter_pids(size_t npids, pid_t *pids);
>
> char *strpbrk_esc(char *str, const char *stopset);
> char *strdup_esc(const char *str);
> +char *strpbrk_esq(char *str, const char *stopset);
> +char *strdup_esq(const char *str);
>
> unsigned int hex(char c);
> char *strreplace_chars(char needle, const char *haystack, const char *replace);
Powered by blists - more mailing lists