diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2015-03-20 16:46:53 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2015-03-21 13:53:36 -0400 |
commit | f208bd8df08d0659ecd9e09e992c79aab26cddfa (patch) | |
tree | 94c8bff840892df70218232788a397c754e45249 | |
parent | e6c76d620379fd65fc0310aee1785ff7b1b10236 (diff) |
perf trace: Handle legacy syscalls tracepoints
Currently the code skips the first field with the expectation that it is 'nr'.
But older kernels do not have the 'nr' field:
field:int nr; offset:8; size:4; signed:1;
Change perf-trace to drop the field if it exists after parsing the format file.
This fixes the off-by-one problem with older kernels (e.g., RHEL6). e.g,
perf-trace shows this for write:
1.515 ( 0.006 ms): dd/4245 write(buf: 2</dev/pts/0>, count: 140733837536224 ) = 26
where 2 is really the fd, the huge number is really the buf address, etc. With
this patch you get the more appropriate:
1.813 ( 0.003 ms): dd/6330 write(fd: 2</dev/pts/0>, buf: 0x7fff22fc81f0, count: 25) = 25
Based-on-a-patch-by: David Ahern <dsahern@gmail.com>
Acked-by: David Ahern <dsahern@gmail.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Borislav Petkov <bp@suse.de>
Cc: Don Zickus <dzickus@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-gvpdave4u2yq2jnzbcdznpvf@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/builtin-trace.c | 18 |
1 files changed, 14 insertions, 4 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 6af6bcec930e..001c6ae9a1b1 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -1135,6 +1135,8 @@ static struct syscall_fmt *syscall_fmt__find(const char *name) | |||
1135 | 1135 | ||
1136 | struct syscall { | 1136 | struct syscall { |
1137 | struct event_format *tp_format; | 1137 | struct event_format *tp_format; |
1138 | int nr_args; | ||
1139 | struct format_field *args; | ||
1138 | const char *name; | 1140 | const char *name; |
1139 | bool filtered; | 1141 | bool filtered; |
1140 | bool is_exit; | 1142 | bool is_exit; |
@@ -1442,14 +1444,14 @@ static int syscall__set_arg_fmts(struct syscall *sc) | |||
1442 | struct format_field *field; | 1444 | struct format_field *field; |
1443 | int idx = 0; | 1445 | int idx = 0; |
1444 | 1446 | ||
1445 | sc->arg_scnprintf = calloc(sc->tp_format->format.nr_fields - 1, sizeof(void *)); | 1447 | sc->arg_scnprintf = calloc(sc->nr_args, sizeof(void *)); |
1446 | if (sc->arg_scnprintf == NULL) | 1448 | if (sc->arg_scnprintf == NULL) |
1447 | return -1; | 1449 | return -1; |
1448 | 1450 | ||
1449 | if (sc->fmt) | 1451 | if (sc->fmt) |
1450 | sc->arg_parm = sc->fmt->arg_parm; | 1452 | sc->arg_parm = sc->fmt->arg_parm; |
1451 | 1453 | ||
1452 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { | 1454 | for (field = sc->args; field; field = field->next) { |
1453 | if (sc->fmt && sc->fmt->arg_scnprintf[idx]) | 1455 | if (sc->fmt && sc->fmt->arg_scnprintf[idx]) |
1454 | sc->arg_scnprintf[idx] = sc->fmt->arg_scnprintf[idx]; | 1456 | sc->arg_scnprintf[idx] = sc->fmt->arg_scnprintf[idx]; |
1455 | else if (field->flags & FIELD_IS_POINTER) | 1457 | else if (field->flags & FIELD_IS_POINTER) |
@@ -1515,6 +1517,14 @@ static int trace__read_syscall_info(struct trace *trace, int id) | |||
1515 | if (sc->tp_format == NULL) | 1517 | if (sc->tp_format == NULL) |
1516 | return -1; | 1518 | return -1; |
1517 | 1519 | ||
1520 | sc->args = sc->tp_format->format.fields; | ||
1521 | sc->nr_args = sc->tp_format->format.nr_fields; | ||
1522 | /* drop nr field - not relevant here; does not exist on older kernels */ | ||
1523 | if (sc->args && strcmp(sc->args->name, "nr") == 0) { | ||
1524 | sc->args = sc->args->next; | ||
1525 | --sc->nr_args; | ||
1526 | } | ||
1527 | |||
1518 | sc->is_exit = !strcmp(name, "exit_group") || !strcmp(name, "exit"); | 1528 | sc->is_exit = !strcmp(name, "exit_group") || !strcmp(name, "exit"); |
1519 | 1529 | ||
1520 | return syscall__set_arg_fmts(sc); | 1530 | return syscall__set_arg_fmts(sc); |
@@ -1537,7 +1547,7 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, | |||
1537 | unsigned char *p; | 1547 | unsigned char *p; |
1538 | unsigned long val; | 1548 | unsigned long val; |
1539 | 1549 | ||
1540 | if (sc->tp_format != NULL) { | 1550 | if (sc->args != NULL) { |
1541 | struct format_field *field; | 1551 | struct format_field *field; |
1542 | u8 bit = 1; | 1552 | u8 bit = 1; |
1543 | struct syscall_arg arg = { | 1553 | struct syscall_arg arg = { |
@@ -1547,7 +1557,7 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, | |||
1547 | .thread = thread, | 1557 | .thread = thread, |
1548 | }; | 1558 | }; |
1549 | 1559 | ||
1550 | for (field = sc->tp_format->format.fields->next; field; | 1560 | for (field = sc->args; field; |
1551 | field = field->next, ++arg.idx, bit <<= 1) { | 1561 | field = field->next, ++arg.idx, bit <<= 1) { |
1552 | if (arg.mask & bit) | 1562 | if (arg.mask & bit) |
1553 | continue; | 1563 | continue; |