Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Masami Hiramatsu and committed by
Arnaldo Carvalho de Melo
c588d158 1e9f9e8a

+51 -23
+16
tools/perf/Documentation/perf-probe.txt
··· 182 182 For details of the SDT, see below. 183 183 https://sourceware.org/gdb/onlinedocs/gdb/Static-Probe-Points.html 184 184 185 + ESCAPED CHARACTER 186 + ----------------- 187 + 188 + In the probe syntax, '=', '@', '+', ':' and ';' are treated as a special character. You can use a backslash ('\') to escape the special characters. 189 + 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. 190 + Note that usually single backslash is consumed by shell, so you might need to pass double backslash (\\) or wrapping with single quotes (\'AAA\@BBB'). 191 + See EXAMPLES how it is used. 192 + 185 193 PROBE ARGUMENT 186 194 -------------- 187 195 Each probe argument follows below syntax. ··· 284 276 Add a USDT probe to a target process running in a different mount namespace 285 277 286 278 ./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 279 + 280 + Add a probe on specific versioned symbol by backslash escape 281 + 282 + ./perf probe -x /lib64/libc-2.25.so 'malloc_get_state\@GLIBC_2.2.5' 283 + 284 + Add a probe in a source file using special characters by backslash escape 285 + 286 + ./perf probe -x /opt/test/a.out 'foo\+bar.c:4' 287 287 288 288 289 289 SEE ALSO
+35 -23
tools/perf/util/probe-event.c
··· 1325 1325 { 1326 1326 char *ptr; 1327 1327 1328 - ptr = strchr(*arg, ':'); 1328 + ptr = strpbrk_esc(*arg, ":"); 1329 1329 if (ptr) { 1330 1330 *ptr = '\0'; 1331 1331 if (!pev->sdt && !is_c_func_name(*arg)) 1332 1332 goto ng_name; 1333 - pev->group = strdup(*arg); 1333 + pev->group = strdup_esc(*arg); 1334 1334 if (!pev->group) 1335 1335 return -ENOMEM; 1336 1336 *arg = ptr + 1; 1337 1337 } else 1338 1338 pev->group = NULL; 1339 - if (!pev->sdt && !is_c_func_name(*arg)) { 1339 + 1340 + pev->event = strdup_esc(*arg); 1341 + if (pev->event == NULL) 1342 + return -ENOMEM; 1343 + 1344 + if (!pev->sdt && !is_c_func_name(pev->event)) { 1345 + zfree(&pev->event); 1340 1346 ng_name: 1347 + zfree(&pev->group); 1341 1348 semantic_error("%s is bad for event name -it must " 1342 1349 "follow C symbol-naming rule.\n", *arg); 1343 1350 return -EINVAL; 1344 1351 } 1345 - pev->event = strdup(*arg); 1346 - if (pev->event == NULL) 1347 - return -ENOMEM; 1348 - 1349 1352 return 0; 1350 1353 } 1351 1354 ··· 1376 1373 arg++; 1377 1374 } 1378 1375 1379 - ptr = strpbrk(arg, ";=@+%"); 1376 + ptr = strpbrk_esc(arg, ";=@+%"); 1380 1377 if (pev->sdt) { 1381 1378 if (ptr) { 1382 1379 if (*ptr != '@') { ··· 1390 1387 pev->target = build_id_cache__origname(tmp); 1391 1388 free(tmp); 1392 1389 } else 1393 - pev->target = strdup(ptr + 1); 1390 + pev->target = strdup_esc(ptr + 1); 1394 1391 if (!pev->target) 1395 1392 return -ENOMEM; 1396 1393 *ptr = '\0'; ··· 1424 1421 * 1425 1422 * Otherwise, we consider arg to be a function specification. 1426 1423 */ 1427 - if (!strpbrk(arg, "+@%") && (ptr = strpbrk(arg, ";:")) != NULL) { 1424 + if (!strpbrk_esc(arg, "+@%")) { 1425 + ptr = strpbrk_esc(arg, ";:"); 1428 1426 /* This is a file spec if it includes a '.' before ; or : */ 1429 - if (memchr(arg, '.', ptr - arg)) 1427 + if (ptr && memchr(arg, '.', ptr - arg)) 1430 1428 file_spec = true; 1431 1429 } 1432 1430 1433 - ptr = strpbrk(arg, ";:+@%"); 1431 + ptr = strpbrk_esc(arg, ";:+@%"); 1434 1432 if (ptr) { 1435 1433 nc = *ptr; 1436 1434 *ptr++ = '\0'; ··· 1440 1436 if (arg[0] == '\0') 1441 1437 tmp = NULL; 1442 1438 else { 1443 - tmp = strdup(arg); 1439 + tmp = strdup_esc(arg); 1444 1440 if (tmp == NULL) 1445 1441 return -ENOMEM; 1446 1442 } ··· 1473 1469 arg = ptr; 1474 1470 c = nc; 1475 1471 if (c == ';') { /* Lazy pattern must be the last part */ 1476 - pp->lazy_line = strdup(arg); 1472 + pp->lazy_line = strdup(arg); /* let leave escapes */ 1477 1473 if (pp->lazy_line == NULL) 1478 1474 return -ENOMEM; 1479 1475 break; 1480 1476 } 1481 - ptr = strpbrk(arg, ";:+@%"); 1477 + ptr = strpbrk_esc(arg, ";:+@%"); 1482 1478 if (ptr) { 1483 1479 nc = *ptr; 1484 1480 *ptr++ = '\0'; ··· 1505 1501 semantic_error("SRC@SRC is not allowed.\n"); 1506 1502 return -EINVAL; 1507 1503 } 1508 - pp->file = strdup(arg); 1504 + pp->file = strdup_esc(arg); 1509 1505 if (pp->file == NULL) 1510 1506 return -ENOMEM; 1511 1507 break; ··· 2807 2803 struct rb_node *tmp; 2808 2804 const char *norm, *ver; 2809 2805 char *buf = NULL; 2806 + bool cut_version = true; 2810 2807 2811 2808 if (map__load(map) < 0) 2812 2809 return 0; 2810 + 2811 + /* If user gives a version, don't cut off the version from symbols */ 2812 + if (strchr(name, '@')) 2813 + cut_version = false; 2813 2814 2814 2815 map__for_each_symbol(map, sym, tmp) { 2815 2816 norm = arch__normalize_symbol_name(sym->name); 2816 2817 if (!norm) 2817 2818 continue; 2818 2819 2819 - /* We don't care about default symbol or not */ 2820 - ver = strchr(norm, '@'); 2821 - if (ver) { 2822 - buf = strndup(norm, ver - norm); 2823 - if (!buf) 2824 - return -ENOMEM; 2825 - norm = buf; 2820 + if (cut_version) { 2821 + /* We don't care about default symbol or not */ 2822 + ver = strchr(norm, '@'); 2823 + if (ver) { 2824 + buf = strndup(norm, ver - norm); 2825 + if (!buf) 2826 + return -ENOMEM; 2827 + norm = buf; 2828 + } 2826 2829 } 2830 + 2827 2831 if (strglobmatch(norm, name)) { 2828 2832 found++; 2829 2833 if (syms && found < probe_conf.max_probes)