From nobody Sun Nov 24 18:01:14 2024 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DBC8D1BF80C; Mon, 4 Nov 2024 16:17:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730737060; cv=none; b=VEs5Brk/ncB6XLralyZP7WYgI74wlAxu+b19md36/yo3rn2qEmnY4rN8CtIX9y+308yxzbfS466huM1TpILmz4Qde07vMFMezee2wXPxaTR3hxkJ0viezNGzaVFh8bZBk//s+Zm6bUYQda8eg1pm1FN45drQ4g/EzkSK4cElbYw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1730737060; c=relaxed/simple; bh=6Izm756VW1TxbakOu4qbxSkidS4sK4VxpC1eBAYg/JA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=mXVnHUWwKQTRxmAztcf1kXxqOgHHa8ZWq4to7jkujYQHGQ15o9GK1zm6qYWsVukH3225MkBAloXgEqlEIYpxkF7nk9ibR+ALq7MqpG3thfd9nygsnH1so76l5dt/GC/iWIaHESoEG9+ONBqSWdb0SFFsj6nP/dczT31MuvZgQOg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=YD4TzWrD; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="YD4TzWrD" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BCBA1C4CECE; Mon, 4 Nov 2024 16:17:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1730737059; bh=6Izm756VW1TxbakOu4qbxSkidS4sK4VxpC1eBAYg/JA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YD4TzWrD1w7okufgMQlAUyHQqjd08nM7wdMHWoHJ9a6RxG13C8NQ3uIAgg6wftkmG lfQLKdJZFt2KVvCkdbkkE9iZy9lr2GFy8HQnCCikuLC7rfxRsY7cHK0OAX3C5Tof58 453d2PECMVAxuDnjk1RdzX21885odNSDI6pwDecjYITOgxU6Jjkmbu0cKM68Whu2gZ l9DdTYNonboibmqfkudCQtz3/ldRMzCjHhU7c1Z1rpEXAb/W6acp2AN2htLQ48b+Qn NzYlBpA9F7Lm2gm0z9oqeACgu9vnjdjk7dSol68Xj1uhZXNO39QNFBuZX24Y1ZFUe3 PTdGUb/jSpo7g== From: "Masami Hiramatsu (Google)" To: Arnaldo Carvalho de Melo , Namhyung Kim Cc: Peter Zijlstra , Ingo Molnar , Masami Hiramatsu , Ian Rogers , Dima Kogan , Alexander Lobakin , Przemek Kitszel , linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 3/4] perf-probe: Introduce quotation marks support Date: Tue, 5 Nov 2024 01:17:35 +0900 Message-ID: <173073705572.2098439.4076465830484831945.stgit@mhiramat.roam.corp.google.com> X-Mailer: git-send-email 2.47.0.163.g1226f6d8fa-goog In-Reply-To: <173073702882.2098439.13342508872190995896.stgit@mhiramat.roam.corp.google.com> References: <173073702882.2098439.13342508872190995896.stgit@mhiramat.roam.corp.google.com> User-Agent: StGit/0.19 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Masami Hiramatsu (Google) 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. For example, without quotes, we have to pass it as below $ perf probe -x cro3 -L "cro3\:\:cmd\:\:servo\:\:run_show" 0 fn run_show(args: &ArgsShow) -> Result<()> { 1 let list =3D ServoList::discover()?; 2 let s =3D 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\" 0 fn run_show(args: &ArgsShow) -> Result<()> { 1 let list =3D ServoList::discover()?; 2 let s =3D list.find_by_serial(&args.servo)?; 3 if args.json { 4 println!("{s}"); Signed-off-by: Masami Hiramatsu (Google) --- 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, c= onst char *module, =20 ret =3D 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 alternati= ve\n"); ret =3D get_alternative_line_range(dinfo, lr, module, user); if (!ret) ret =3D debuginfo__find_line_range(dinfo, lr); @@ -1078,7 +1079,7 @@ static int __show_line_range(struct line_range *lr, c= onst 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; } =20 @@ -1187,7 +1188,7 @@ static int show_available_vars_at(struct debuginfo *d= info, pr_err("Failed to find the address of %s\n", buf); ret =3D -ENOENT; } else - pr_warning("Debuginfo analysis failed.\n"); + pr_warning("Debuginfo analysis failed(2).\n"); goto end; } =20 @@ -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 =3D strdup(arg); + char *buf =3D strdup(arg); + char *p; int err; =20 - if (!name) + if (!buf) return -ENOMEM; =20 lr->start =3D 0; lr->end =3D INT_MAX; =20 - range =3D strpbrk_esc(name, ":"); - if (range) { - *range++ =3D '\0'; + pr_debug2("Input line range: %s\n", buf); + p =3D strpbrk_esq(buf, ":"); + if (p) { + if (p =3D=3D buf) { + semantic_error("No file/function name in '%s'.\n", p); + err =3D -EINVAL; + goto err; + } + *(p++) =3D '\0'; =20 - err =3D parse_line_num(&range, &lr->start, "start line"); + err =3D parse_line_num(&p, &lr->start, "start line"); if (err) goto err; =20 - if (*range =3D=3D '+' || *range =3D=3D '-') { - const char c =3D *range++; + if (*p =3D=3D '+' || *p =3D=3D '-') { + const char c =3D *(p++); =20 - err =3D parse_line_num(&range, &lr->end, "end line"); + err =3D parse_line_num(&p, &lr->end, "end line"); if (err) goto err; =20 @@ -1390,28 +1400,22 @@ int parse_line_range_desc(const char *arg, struct l= ine_range *lr) " than end line.\n"); goto err; } - if (*range !=3D '\0') { - semantic_error("Tailing with invalid str '%s'.\n", range); + if (*p !=3D '\0') { + semantic_error("Tailing with invalid str '%s'.\n", p); goto err; } } =20 - file =3D strpbrk_esc(name, "@"); - if (file) { - *file =3D '\0'; - lr->file =3D strdup_esc(++file); - if (lr->file =3D=3D NULL) { - err =3D -ENOMEM; - goto err; - } - if (*name !=3D '\0') - lr->function =3D name; - } else - lr->function =3D name; + p =3D strpbrk_esq(buf, "@"); + if (p) { + *(p++) =3D '\0'; + lr->file =3D strdup_esq(p); + } + lr->function =3D strdup_esq(buf); + err =3D 0; =20 - return 0; err: - free(name); + free(buf); return err; } =20 @@ -1419,19 +1423,19 @@ static int parse_perf_probe_event_name(char **arg, = struct perf_probe_event *pev) { char *ptr; =20 - ptr =3D strpbrk_esc(*arg, ":"); + ptr =3D strpbrk_esq(*arg, ":"); if (ptr) { *ptr =3D '\0'; if (!pev->sdt && !is_c_func_name(*arg)) goto ng_name; - pev->group =3D strdup_esc(*arg); + pev->group =3D strdup_esq(*arg); if (!pev->group) return -ENOMEM; *arg =3D ptr + 1; } else pev->group =3D NULL; =20 - pev->event =3D strdup_esc(*arg); + pev->event =3D strdup_esq(*arg); if (pev->event =3D=3D NULL) return -ENOMEM; =20 @@ -1470,7 +1474,7 @@ static int parse_perf_probe_point(char *arg, struct p= erf_probe_event *pev) arg++; } =20 - ptr =3D strpbrk_esc(arg, ";=3D@+%"); + ptr =3D strpbrk_esq(arg, ";=3D@+%"); if (pev->sdt) { if (ptr) { if (*ptr !=3D '@') { @@ -1484,7 +1488,7 @@ static int parse_perf_probe_point(char *arg, struct p= erf_probe_event *pev) pev->target =3D build_id_cache__origname(tmp); free(tmp); } else - pev->target =3D strdup_esc(ptr + 1); + pev->target =3D strdup_esq(ptr + 1); if (!pev->target) return -ENOMEM; *ptr =3D '\0'; @@ -1518,7 +1522,7 @@ static int parse_perf_probe_point(char *arg, struct p= erf_probe_event *pev) arg++; } =20 - ptr =3D strpbrk_esc(arg, ";:+@%"); + ptr =3D strpbrk_esq(arg, ";:+@%"); if (ptr) { nc =3D *ptr; *ptr++ =3D '\0'; @@ -1527,7 +1531,7 @@ static int parse_perf_probe_point(char *arg, struct p= erf_probe_event *pev) if (arg[0] =3D=3D '\0') tmp =3D NULL; else { - tmp =3D strdup_esc(arg); + tmp =3D strdup_esq(arg); if (tmp =3D=3D NULL) return -ENOMEM; } @@ -1565,7 +1569,7 @@ static int parse_perf_probe_point(char *arg, struct p= erf_probe_event *pev) return -ENOMEM; break; } - ptr =3D strpbrk_esc(arg, ";:+@%"); + ptr =3D strpbrk_esq(arg, ";:+@%"); if (ptr) { nc =3D *ptr; *ptr++ =3D '\0'; @@ -1592,7 +1596,7 @@ static int parse_perf_probe_point(char *arg, struct p= erf_probe_event *pev) semantic_error("SRC@SRC is not allowed.\n"); return -EINVAL; } - pp->file =3D strdup_esc(arg); + pp->file =3D strdup_esq(arg); if (pp->file =3D=3D 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 =3D { .data =3D (void *)&lf, .retval =3D 0}; =20 + 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"); } =20 + pr_debug2("Search function '%s' in DIE tree\n", lr->function); /* Loop on CUs (Compilation Unit) */ while (!lf.found && ret >=3D 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; } =20 +/* Like strpbrk_esc(), but not break if it is quoted with single/double qu= otes */ +char *strpbrk_esq(char *str, const char *stopset) +{ + char *_stopset =3D NULL; + char *ptr; + const char *squote =3D "'"; + const char *dquote =3D "\""; + + if (asprintf(&_stopset, "%s%c%c", stopset, *squote, *dquote) < 0) + return NULL; + + do { + ptr =3D strpbrk_esc(str, _stopset); + if (!ptr) + break; + if (*ptr =3D=3D *squote) + ptr =3D strpbrk_esc(ptr + 1, squote); + else if (*ptr =3D=3D *dquote) + ptr =3D strpbrk_esc(ptr + 1, dquote); + else + break; + str =3D 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; } =20 +/* Remove backslash right before quote and return next quote address. */ +static char *remove_consumed_esc(char *str, int len, int quote) +{ + char *ptr =3D str, *end =3D str + len; + + while (*ptr !=3D quote && ptr < end) { + if (*ptr =3D=3D '\\' && *(ptr + 1) =3D=3D quote) { + memmove(ptr, ptr + 1, end - (ptr + 1)); + /* now *ptr is `quote`. */ + end--; + } + ptr++; + } + + return *ptr =3D=3D 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 =3D strpbrk_esc((char *)str, "\"'"); + if (!d) + return strdup_esc(str); + + ret =3D strdup(str); + if (!ret) + return NULL; + + d =3D ret; + do { + d =3D strpbrk(d, "\\\"\'"); + if (!d) + break; + + if (*d =3D=3D '"' || *d =3D=3D '\'') { + /* This is non-escaped quote */ + int quote =3D *d; + int len =3D 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 =3D remove_consumed_esc(d, len, quote); + if (!d) + goto error; + memmove(d, d + 1, strlen(d + 1) + 1); + } + if (*d =3D=3D '\\') { + memmove(d, d + 1, strlen(d + 1) + 1); + if (*d =3D=3D '\\') { + /* double backslash -- keep the second one. */ + d++; + } + } + } while (*d !=3D '\0'); + + return ret; + +error: + free(ret); + return NULL; +} + unsigned int hex(char c) { if (c >=3D '0' && c <=3D '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); =20 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); =20 unsigned int hex(char c); char *strreplace_chars(char needle, const char *haystack, const char *repl= ace);