All of lore.kernel.org
 help / color / mirror / Atom feed
From: Arnaldo Carvalho de Melo <acme@kernel.org>
To: "Masami Hiramatsu (Google)" <mhiramat@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>,
	Peter Zijlstra <peterz@infradead.org>,
	Ingo Molnar <mingo@redhat.com>, Ian Rogers <irogers@google.com>,
	Dima Kogan <dima@secretsauce.net>,
	Alexander Lobakin <aleksander.lobakin@intel.com>,
	Przemek Kitszel <przemyslaw.kitszel@intel.com>,
	linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH 3/4] perf-probe: Introduce quotation marks support
Date: Mon, 4 Nov 2024 17:23:11 -0300	[thread overview]
Message-ID: <ZyktL05NmqRTr213@x1> (raw)
In-Reply-To: <173073705572.2098439.4076465830484831945.stgit@mhiramat.roam.corp.google.com>

On Tue, Nov 05, 2024 at 01:17:35AM +0900, Masami Hiramatsu (Google) wrote:
> From: Masami Hiramatsu (Google) <mhiramat@kernel.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@/work/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@/work/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@kernel.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);

  reply	other threads:[~2024-11-04 20:23 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-11-04 16:17 [PATCH 0/4] perf-probe: Improbe non-C language support Masami Hiramatsu (Google)
2024-11-04 16:17 ` [PATCH 1/4] perf-probe: Fix to ignore escaped characters in --lines option Masami Hiramatsu (Google)
2024-11-04 16:17 ` [PATCH 2/4] perf-probe: Require '@' prefix for filename always Masami Hiramatsu (Google)
2024-11-04 20:10   ` Arnaldo Carvalho de Melo
2024-11-05  9:28     ` Masami Hiramatsu
2024-11-05  9:36       ` Masami Hiramatsu
2024-11-04 16:17 ` [PATCH 3/4] perf-probe: Introduce quotation marks support Masami Hiramatsu (Google)
2024-11-04 20:23   ` Arnaldo Carvalho de Melo [this message]
2024-11-05  9:29     ` Masami Hiramatsu
2024-11-04 16:17 ` [PATCH 4/4] perf-probe: Replace unacceptable characters when generating event name Masami Hiramatsu (Google)
2024-11-04 20:34   ` Arnaldo Carvalho de Melo
2024-11-05  8:27     ` Masami Hiramatsu
2024-11-04 18:56 ` [PATCH 0/4] perf-probe: Improbe non-C language support Arnaldo Carvalho de Melo
2024-11-04 20:08   ` Arnaldo Carvalho de Melo
2024-11-05  9:38     ` Masami Hiramatsu
2024-11-05  9:48   ` Masami Hiramatsu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ZyktL05NmqRTr213@x1 \
    --to=acme@kernel.org \
    --cc=aleksander.lobakin@intel.com \
    --cc=dima@secretsauce.net \
    --cc=irogers@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=mhiramat@kernel.org \
    --cc=mingo@redhat.com \
    --cc=namhyung@kernel.org \
    --cc=peterz@infradead.org \
    --cc=przemyslaw.kitszel@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.