perf probe: Support escaped character in parser

Support the special characters escaped by '\' in parser.  This allows
user to specify versions directly like below.

  =====
  # ./perf probe -x /lib64/libc-2.25.so malloc_get_state\\@GLIBC_2.2.5
  Added new event:
    probe_libc:malloc_get_state (on malloc_get_state@GLIBC_2.2.5 in /usr/lib64/libc-2.25.so)

  You can now use it in all perf tools, such as:

	  perf record -e probe_libc:malloc_get_state -aR sleep 1

  =====

Or, you can use separators in source filename, e.g.

  =====
  # ./perf probe -x /opt/test/a.out foo+bar.c:3
  Semantic error :There is non-digit character in offset.
    Error: Command Parse Error.
  =====

Usually "+" in source file cause parser error, but

  =====
  # ./perf probe -x /opt/test/a.out foo\\+bar.c:4
  Added new event:
    probe_a:main         (on @foo+bar.c:4 in /opt/test/a.out)

  You can now use it in all perf tools, such as:

	  perf record -e probe_a:main -aR sleep 1
  =====

escaped "\+" allows you to specify that.

Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Reviewed-by: Thomas Richter <tmricht@linux.vnet.ibm.com>
Acked-by: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
Cc: Paul Clarke <pc@us.ibm.com>
Cc: bhargavb <bhargavaramudu@gmail.com>
Cc: linux-rt-users@vger.kernel.org
Link: http://lkml.kernel.org/r/151309111236.18107.5634753157435343410.stgit@devbox
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Masami Hiramatsu 2017-12-13 00:05:12 +09:00 committed by Arnaldo Carvalho de Melo
parent 1e9f9e8af0
commit c588d15812
2 changed files with 51 additions and 23 deletions

View File

@ -182,6 +182,14 @@ Note that before using the SDT event, the target binary (on which SDT events are
For details of the SDT, see below.
https://sourceware.org/gdb/onlinedocs/gdb/Static-Probe-Points.html
ESCAPED CHARACTER
-----------------
In the probe syntax, '=', '@', '+', ':' and ';' are treated as a special character. You can use a backslash ('\') to escape the special characters.
This is useful if you need to probe on a specific versioned symbols, like @GLIBC_... suffixes, or also you need to specify a source file which includes the special characters.
Note that usually single backslash is consumed by shell, so you might need to pass double backslash (\\) or wrapping with single quotes (\'AAA\@BBB').
See EXAMPLES how it is used.
PROBE ARGUMENT
--------------
Each probe argument follows below syntax.
@ -277,6 +285,14 @@ Add a USDT probe to a target process running in a different mount namespace
./perf probe --target-ns <target pid> -x /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.121-0.b13.el7_3.x86_64/jre/lib/amd64/server/libjvm.so %sdt_hotspot:thread__sleep__end
Add a probe on specific versioned symbol by backslash escape
./perf probe -x /lib64/libc-2.25.so 'malloc_get_state\@GLIBC_2.2.5'
Add a probe in a source file using special characters by backslash escape
./perf probe -x /opt/test/a.out 'foo\+bar.c:4'
SEE ALSO
--------

View File

@ -1325,27 +1325,30 @@ static int parse_perf_probe_event_name(char **arg, struct perf_probe_event *pev)
{
char *ptr;
ptr = strchr(*arg, ':');
ptr = strpbrk_esc(*arg, ":");
if (ptr) {
*ptr = '\0';
if (!pev->sdt && !is_c_func_name(*arg))
goto ng_name;
pev->group = strdup(*arg);
pev->group = strdup_esc(*arg);
if (!pev->group)
return -ENOMEM;
*arg = ptr + 1;
} else
pev->group = NULL;
if (!pev->sdt && !is_c_func_name(*arg)) {
pev->event = strdup_esc(*arg);
if (pev->event == NULL)
return -ENOMEM;
if (!pev->sdt && !is_c_func_name(pev->event)) {
zfree(&pev->event);
ng_name:
zfree(&pev->group);
semantic_error("%s is bad for event name -it must "
"follow C symbol-naming rule.\n", *arg);
return -EINVAL;
}
pev->event = strdup(*arg);
if (pev->event == NULL)
return -ENOMEM;
return 0;
}
@ -1373,7 +1376,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
arg++;
}
ptr = strpbrk(arg, ";=@+%");
ptr = strpbrk_esc(arg, ";=@+%");
if (pev->sdt) {
if (ptr) {
if (*ptr != '@') {
@ -1387,7 +1390,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(ptr + 1);
pev->target = strdup_esc(ptr + 1);
if (!pev->target)
return -ENOMEM;
*ptr = '\0';
@ -1421,13 +1424,14 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
*
* Otherwise, we consider arg to be a function specification.
*/
if (!strpbrk(arg, "+@%") && (ptr = strpbrk(arg, ";:")) != NULL) {
if (!strpbrk_esc(arg, "+@%")) {
ptr = strpbrk_esc(arg, ";:");
/* This is a file spec if it includes a '.' before ; or : */
if (memchr(arg, '.', ptr - arg))
if (ptr && memchr(arg, '.', ptr - arg))
file_spec = true;
}
ptr = strpbrk(arg, ";:+@%");
ptr = strpbrk_esc(arg, ";:+@%");
if (ptr) {
nc = *ptr;
*ptr++ = '\0';
@ -1436,7 +1440,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
if (arg[0] == '\0')
tmp = NULL;
else {
tmp = strdup(arg);
tmp = strdup_esc(arg);
if (tmp == NULL)
return -ENOMEM;
}
@ -1469,12 +1473,12 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
arg = ptr;
c = nc;
if (c == ';') { /* Lazy pattern must be the last part */
pp->lazy_line = strdup(arg);
pp->lazy_line = strdup(arg); /* let leave escapes */
if (pp->lazy_line == NULL)
return -ENOMEM;
break;
}
ptr = strpbrk(arg, ";:+@%");
ptr = strpbrk_esc(arg, ";:+@%");
if (ptr) {
nc = *ptr;
*ptr++ = '\0';
@ -1501,7 +1505,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(arg);
pp->file = strdup_esc(arg);
if (pp->file == NULL)
return -ENOMEM;
break;
@ -2803,23 +2807,31 @@ static int find_probe_functions(struct map *map, char *name,
struct rb_node *tmp;
const char *norm, *ver;
char *buf = NULL;
bool cut_version = true;
if (map__load(map) < 0)
return 0;
/* If user gives a version, don't cut off the version from symbols */
if (strchr(name, '@'))
cut_version = false;
map__for_each_symbol(map, sym, tmp) {
norm = arch__normalize_symbol_name(sym->name);
if (!norm)
continue;
/* We don't care about default symbol or not */
ver = strchr(norm, '@');
if (ver) {
buf = strndup(norm, ver - norm);
if (!buf)
return -ENOMEM;
norm = buf;
if (cut_version) {
/* We don't care about default symbol or not */
ver = strchr(norm, '@');
if (ver) {
buf = strndup(norm, ver - norm);
if (!buf)
return -ENOMEM;
norm = buf;
}
}
if (strglobmatch(norm, name)) {
found++;
if (syms && found < probe_conf.max_probes)