diff options
Diffstat (limited to 'lib/hexdump.c')
-rw-r--r-- | lib/hexdump.c | 149 |
1 files changed, 121 insertions, 28 deletions
diff --git a/lib/hexdump.c b/lib/hexdump.c index e6da5b7fc29a..473f5aed6cae 100644 --- a/lib/hexdump.c +++ b/lib/hexdump.c | |||
@@ -16,42 +16,98 @@ | |||
16 | * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory | 16 | * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory |
17 | * @buf: data blob to dump | 17 | * @buf: data blob to dump |
18 | * @len: number of bytes in the @buf | 18 | * @len: number of bytes in the @buf |
19 | * @rowsize: number of bytes to print per line; must be 16 or 32 | ||
20 | * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1) | ||
19 | * @linebuf: where to put the converted data | 21 | * @linebuf: where to put the converted data |
20 | * @linebuflen: total size of @linebuf, including space for terminating NUL | 22 | * @linebuflen: total size of @linebuf, including space for terminating NUL |
23 | * @ascii: include ASCII after the hex output | ||
21 | * | 24 | * |
22 | * hex_dump_to_buffer() works on one "line" of output at a time, i.e., | 25 | * hex_dump_to_buffer() works on one "line" of output at a time, i.e., |
23 | * 16 bytes of input data converted to hex + ASCII output. | 26 | * 16 or 32 bytes of input data converted to hex + ASCII output. |
24 | * | 27 | * |
25 | * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data | 28 | * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data |
26 | * to a hex + ASCII dump at the supplied memory location. | 29 | * to a hex + ASCII dump at the supplied memory location. |
27 | * The converted output is always NUL-terminated. | 30 | * The converted output is always NUL-terminated. |
28 | * | 31 | * |
29 | * E.g.: | 32 | * E.g.: |
30 | * hex_dump_to_buffer(frame->data, frame->len, linebuf, sizeof(linebuf)); | 33 | * hex_dump_to_buffer(frame->data, frame->len, 16, 1, |
34 | * linebuf, sizeof(linebuf), 1); | ||
31 | * | 35 | * |
32 | * example output buffer: | 36 | * example output buffer: |
33 | * 40414243 44454647 48494a4b 4c4d4e4f @ABCDEFGHIJKLMNO | 37 | * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO |
34 | */ | 38 | */ |
35 | void hex_dump_to_buffer(const void *buf, size_t len, char *linebuf, | 39 | void hex_dump_to_buffer(const void *buf, size_t len, int rowsize, |
36 | size_t linebuflen) | 40 | int groupsize, char *linebuf, size_t linebuflen, |
41 | bool ascii) | ||
37 | { | 42 | { |
38 | const u8 *ptr = buf; | 43 | const u8 *ptr = buf; |
39 | u8 ch; | 44 | u8 ch; |
40 | int j, lx = 0; | 45 | int j, lx = 0; |
46 | int ascii_column; | ||
41 | 47 | ||
42 | for (j = 0; (j < 16) && (j < len) && (lx + 3) < linebuflen; j++) { | 48 | if (rowsize != 16 && rowsize != 32) |
43 | if (j && !(j % 4)) | 49 | rowsize = 16; |
50 | |||
51 | if (!len) | ||
52 | goto nil; | ||
53 | if (len > rowsize) /* limit to one line at a time */ | ||
54 | len = rowsize; | ||
55 | if ((len % groupsize) != 0) /* no mixed size output */ | ||
56 | groupsize = 1; | ||
57 | |||
58 | switch (groupsize) { | ||
59 | case 8: { | ||
60 | const u64 *ptr8 = buf; | ||
61 | int ngroups = len / groupsize; | ||
62 | |||
63 | for (j = 0; j < ngroups; j++) | ||
64 | lx += scnprintf(linebuf + lx, linebuflen - lx, | ||
65 | "%16.16llx ", (unsigned long long)*(ptr8 + j)); | ||
66 | ascii_column = 17 * ngroups + 2; | ||
67 | break; | ||
68 | } | ||
69 | |||
70 | case 4: { | ||
71 | const u32 *ptr4 = buf; | ||
72 | int ngroups = len / groupsize; | ||
73 | |||
74 | for (j = 0; j < ngroups; j++) | ||
75 | lx += scnprintf(linebuf + lx, linebuflen - lx, | ||
76 | "%8.8x ", *(ptr4 + j)); | ||
77 | ascii_column = 9 * ngroups + 2; | ||
78 | break; | ||
79 | } | ||
80 | |||
81 | case 2: { | ||
82 | const u16 *ptr2 = buf; | ||
83 | int ngroups = len / groupsize; | ||
84 | |||
85 | for (j = 0; j < ngroups; j++) | ||
86 | lx += scnprintf(linebuf + lx, linebuflen - lx, | ||
87 | "%4.4x ", *(ptr2 + j)); | ||
88 | ascii_column = 5 * ngroups + 2; | ||
89 | break; | ||
90 | } | ||
91 | |||
92 | default: | ||
93 | for (j = 0; (j < rowsize) && (j < len) && (lx + 4) < linebuflen; | ||
94 | j++) { | ||
95 | ch = ptr[j]; | ||
96 | linebuf[lx++] = hex_asc(ch >> 4); | ||
97 | linebuf[lx++] = hex_asc(ch & 0x0f); | ||
44 | linebuf[lx++] = ' '; | 98 | linebuf[lx++] = ' '; |
45 | ch = ptr[j]; | 99 | } |
46 | linebuf[lx++] = hex_asc(ch >> 4); | 100 | ascii_column = 3 * rowsize + 2; |
47 | linebuf[lx++] = hex_asc(ch & 0x0f); | 101 | break; |
48 | } | 102 | } |
49 | if ((lx + 2) < linebuflen) { | 103 | if (!ascii) |
50 | linebuf[lx++] = ' '; | 104 | goto nil; |
105 | |||
106 | while (lx < (linebuflen - 1) && lx < (ascii_column - 1)) | ||
51 | linebuf[lx++] = ' '; | 107 | linebuf[lx++] = ' '; |
52 | } | 108 | for (j = 0; (j < rowsize) && (j < len) && (lx + 2) < linebuflen; j++) |
53 | for (j = 0; (j < 16) && (j < len) && (lx + 2) < linebuflen; j++) | ||
54 | linebuf[lx++] = isprint(ptr[j]) ? ptr[j] : '.'; | 109 | linebuf[lx++] = isprint(ptr[j]) ? ptr[j] : '.'; |
110 | nil: | ||
55 | linebuf[lx++] = '\0'; | 111 | linebuf[lx++] = '\0'; |
56 | } | 112 | } |
57 | EXPORT_SYMBOL(hex_dump_to_buffer); | 113 | EXPORT_SYMBOL(hex_dump_to_buffer); |
@@ -59,46 +115,83 @@ EXPORT_SYMBOL(hex_dump_to_buffer); | |||
59 | /** | 115 | /** |
60 | * print_hex_dump - print a text hex dump to syslog for a binary blob of data | 116 | * print_hex_dump - print a text hex dump to syslog for a binary blob of data |
61 | * @level: kernel log level (e.g. KERN_DEBUG) | 117 | * @level: kernel log level (e.g. KERN_DEBUG) |
118 | * @prefix_str: string to prefix each line with; | ||
119 | * caller supplies trailing spaces for alignment if desired | ||
62 | * @prefix_type: controls whether prefix of an offset, address, or none | 120 | * @prefix_type: controls whether prefix of an offset, address, or none |
63 | * is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE) | 121 | * is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE) |
122 | * @rowsize: number of bytes to print per line; must be 16 or 32 | ||
123 | * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1) | ||
64 | * @buf: data blob to dump | 124 | * @buf: data blob to dump |
65 | * @len: number of bytes in the @buf | 125 | * @len: number of bytes in the @buf |
126 | * @ascii: include ASCII after the hex output | ||
66 | * | 127 | * |
67 | * Given a buffer of u8 data, print_hex_dump() prints a hex + ASCII dump | 128 | * Given a buffer of u8 data, print_hex_dump() prints a hex + ASCII dump |
68 | * to the kernel log at the specified kernel log level, with an optional | 129 | * to the kernel log at the specified kernel log level, with an optional |
69 | * leading prefix. | 130 | * leading prefix. |
70 | * | 131 | * |
132 | * print_hex_dump() works on one "line" of output at a time, i.e., | ||
133 | * 16 or 32 bytes of input data converted to hex + ASCII output. | ||
134 | * print_hex_dump() iterates over the entire input @buf, breaking it into | ||
135 | * "line size" chunks to format and print. | ||
136 | * | ||
71 | * E.g.: | 137 | * E.g.: |
72 | * print_hex_dump(KERN_DEBUG, DUMP_PREFIX_ADDRESS, frame->data, frame->len); | 138 | * print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS, |
139 | * 16, 1, frame->data, frame->len, 1); | ||
73 | * | 140 | * |
74 | * Example output using %DUMP_PREFIX_OFFSET: | 141 | * Example output using %DUMP_PREFIX_OFFSET and 1-byte mode: |
75 | * 0009ab42: 40414243 44454647 48494a4b 4c4d4e4f @ABCDEFGHIJKLMNO | 142 | * 0009ab42: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO |
76 | * Example output using %DUMP_PREFIX_ADDRESS: | 143 | * Example output using %DUMP_PREFIX_ADDRESS and 4-byte mode: |
77 | * ffffffff88089af0: 70717273 74757677 78797a7b 7c7d7e7f pqrstuvwxyz{|}~. | 144 | * ffffffff88089af0: 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~. |
78 | */ | 145 | */ |
79 | void print_hex_dump(const char *level, int prefix_type, void *buf, size_t len) | 146 | void print_hex_dump(const char *level, const char *prefix_str, int prefix_type, |
147 | int rowsize, int groupsize, | ||
148 | void *buf, size_t len, bool ascii) | ||
80 | { | 149 | { |
81 | u8 *ptr = buf; | 150 | u8 *ptr = buf; |
82 | int i, linelen, remaining = len; | 151 | int i, linelen, remaining = len; |
83 | unsigned char linebuf[100]; | 152 | unsigned char linebuf[200]; |
84 | 153 | ||
85 | for (i = 0; i < len; i += 16) { | 154 | if (rowsize != 16 && rowsize != 32) |
86 | linelen = min(remaining, 16); | 155 | rowsize = 16; |
87 | remaining -= 16; | 156 | |
88 | hex_dump_to_buffer(ptr + i, linelen, linebuf, sizeof(linebuf)); | 157 | for (i = 0; i < len; i += rowsize) { |
158 | linelen = min(remaining, rowsize); | ||
159 | remaining -= rowsize; | ||
160 | hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize, | ||
161 | linebuf, sizeof(linebuf), ascii); | ||
89 | 162 | ||
90 | switch (prefix_type) { | 163 | switch (prefix_type) { |
91 | case DUMP_PREFIX_ADDRESS: | 164 | case DUMP_PREFIX_ADDRESS: |
92 | printk("%s%*p: %s\n", level, | 165 | printk("%s%s%*p: %s\n", level, prefix_str, |
93 | (int)(2 * sizeof(void *)), ptr + i, linebuf); | 166 | (int)(2 * sizeof(void *)), ptr + i, linebuf); |
94 | break; | 167 | break; |
95 | case DUMP_PREFIX_OFFSET: | 168 | case DUMP_PREFIX_OFFSET: |
96 | printk("%s%.8x: %s\n", level, i, linebuf); | 169 | printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf); |
97 | break; | 170 | break; |
98 | default: | 171 | default: |
99 | printk("%s%s\n", level, linebuf); | 172 | printk("%s%s%s\n", level, prefix_str, linebuf); |
100 | break; | 173 | break; |
101 | } | 174 | } |
102 | } | 175 | } |
103 | } | 176 | } |
104 | EXPORT_SYMBOL(print_hex_dump); | 177 | EXPORT_SYMBOL(print_hex_dump); |
178 | |||
179 | /** | ||
180 | * print_hex_dump_bytes - shorthand form of print_hex_dump() with default params | ||
181 | * @prefix_str: string to prefix each line with; | ||
182 | * caller supplies trailing spaces for alignment if desired | ||
183 | * @prefix_type: controls whether prefix of an offset, address, or none | ||
184 | * is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE) | ||
185 | * @buf: data blob to dump | ||
186 | * @len: number of bytes in the @buf | ||
187 | * | ||
188 | * Calls print_hex_dump(), with log level of KERN_DEBUG, | ||
189 | * rowsize of 16, groupsize of 1, and ASCII output included. | ||
190 | */ | ||
191 | void print_hex_dump_bytes(const char *prefix_str, int prefix_type, | ||
192 | void *buf, size_t len) | ||
193 | { | ||
194 | print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, 16, 1, | ||
195 | buf, len, 1); | ||
196 | } | ||
197 | EXPORT_SYMBOL(print_hex_dump_bytes); | ||