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

perf tests: objdump output can contain multi byte chunks

objdump's raw insn output can vary across architectures on the number of
bytes per chunk (bpc) displayed and their endianness.

The code-reading test relied on reading objdump output as 1 bpc. Kaixu
Xia reported test failure on ARM64, where objdump displays 4 bpc:

70c48: f90027bf str xzr, [x29,#72]
70c4c: 91224000 add x0, x0, #0x890
70c50: f90023a0 str x0, [x29,#64]

This patch adds support to read raw insn output for any bpc length.
In case of 2+ bpc it also guesses objdump's display endian.

Reported-and-Tested-by: Kaixu Xia <xiakaixu@huawei.com>
Signed-off-by: Jan Stancek <jstancek@redhat.com>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/07f0f7bcbda78deb423298708ef9b6a54d6b92bd.1452592712.git.jstancek@redhat.com
[ Fix up pr_fmt() call to use %zd for size_t variables, fixing the build on Ubuntu cross-compiling to armhf and ppc64 ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Jan Stancek and committed by
Arnaldo Carvalho de Melo
b2d0dbf0 b6f35ed7

+71 -29
+71 -29
tools/perf/tests/code-reading.c
··· 33 33 return c - 'A' + 10; 34 34 } 35 35 36 - static size_t read_objdump_line(const char *line, size_t line_len, void *buf, 37 - size_t len) 36 + static size_t read_objdump_chunk(const char **line, unsigned char **buf, 37 + size_t *buf_len) 38 + { 39 + size_t bytes_read = 0; 40 + unsigned char *chunk_start = *buf; 41 + 42 + /* Read bytes */ 43 + while (*buf_len > 0) { 44 + char c1, c2; 45 + 46 + /* Get 2 hex digits */ 47 + c1 = *(*line)++; 48 + if (!isxdigit(c1)) 49 + break; 50 + c2 = *(*line)++; 51 + if (!isxdigit(c2)) 52 + break; 53 + 54 + /* Store byte and advance buf */ 55 + **buf = (hex(c1) << 4) | hex(c2); 56 + (*buf)++; 57 + (*buf_len)--; 58 + bytes_read++; 59 + 60 + /* End of chunk? */ 61 + if (isspace(**line)) 62 + break; 63 + } 64 + 65 + /* 66 + * objdump will display raw insn as LE if code endian 67 + * is LE and bytes_per_chunk > 1. In that case reverse 68 + * the chunk we just read. 69 + * 70 + * see disassemble_bytes() at binutils/objdump.c for details 71 + * how objdump chooses display endian) 72 + */ 73 + if (bytes_read > 1 && !bigendian()) { 74 + unsigned char *chunk_end = chunk_start + bytes_read - 1; 75 + unsigned char tmp; 76 + 77 + while (chunk_start < chunk_end) { 78 + tmp = *chunk_start; 79 + *chunk_start = *chunk_end; 80 + *chunk_end = tmp; 81 + chunk_start++; 82 + chunk_end--; 83 + } 84 + } 85 + 86 + return bytes_read; 87 + } 88 + 89 + static size_t read_objdump_line(const char *line, unsigned char *buf, 90 + size_t buf_len) 38 91 { 39 92 const char *p; 40 - size_t i, j = 0; 93 + size_t ret, bytes_read = 0; 41 94 42 95 /* Skip to a colon */ 43 96 p = strchr(line, ':'); 44 97 if (!p) 45 98 return 0; 46 - i = p + 1 - line; 99 + p++; 47 100 48 - /* Read bytes */ 49 - while (j < len) { 50 - char c1, c2; 51 - 52 - /* Skip spaces */ 53 - for (; i < line_len; i++) { 54 - if (!isspace(line[i])) 55 - break; 56 - } 57 - /* Get 2 hex digits */ 58 - if (i >= line_len || !isxdigit(line[i])) 101 + /* Skip initial spaces */ 102 + while (*p) { 103 + if (!isspace(*p)) 59 104 break; 60 - c1 = line[i++]; 61 - if (i >= line_len || !isxdigit(line[i])) 62 - break; 63 - c2 = line[i++]; 64 - /* Followed by a space */ 65 - if (i < line_len && line[i] && !isspace(line[i])) 66 - break; 67 - /* Store byte */ 68 - *(unsigned char *)buf = (hex(c1) << 4) | hex(c2); 69 - buf += 1; 70 - j++; 105 + p++; 71 106 } 107 + 108 + do { 109 + ret = read_objdump_chunk(&p, &buf, &buf_len); 110 + bytes_read += ret; 111 + p++; 112 + } while (ret > 0); 113 + 72 114 /* return number of successfully read bytes */ 73 - return j; 115 + return bytes_read; 74 116 } 75 117 76 118 static int read_objdump_output(FILE *f, void *buf, size_t *len, u64 start_addr) ··· 137 95 } 138 96 139 97 /* read objdump data into temporary buffer */ 140 - read_bytes = read_objdump_line(line, ret, tmp, sizeof(tmp)); 98 + read_bytes = read_objdump_line(line, tmp, sizeof(tmp)); 141 99 if (!read_bytes) 142 100 continue; 143 101 ··· 194 152 195 153 ret = read_objdump_output(f, buf, &len, addr); 196 154 if (len) { 197 - pr_debug("objdump read too few bytes\n"); 155 + pr_debug("objdump read too few bytes: %zd\n", len); 198 156 if (!ret) 199 157 ret = len; 200 158 }