Commit c7909234 authored by Randy Dunlap's avatar Randy Dunlap Committed by Linus Torvalds

hexdump: more output formatting

Add a prefix string parameter.  Callers are responsible for any string
length/alignment that they want to see in the output.  I.e., callers should
pad strings to achieve alignment if they want that.

Add rowsize parameter.  This is the number of raw data bytes to be printed
per line.  Must be 16 or 32.

Add a groupsize parameter.  This allows callers to dump values as 1-byte,
2-byte, 4-byte, or 8-byte numbers.  Default is 1-byte numbers.  If the
total length is not an even multiple of groupsize, 1-byte numbers are
printed.

Add an "ascii" output parameter.  This causes ASCII data output following
the hex data output.

Clean up some doc examples.

Align the ASCII output on all lines that are produced by one call.

Add a new interface, print_hex_dump_bytes(), that is a shortcut to
print_hex_dump(), using default parameter values to print 16 bytes in
byte-size chunks of hex + ASCII output, using printk level KERN_DEBUG.
Signed-off-by: default avatarRandy Dunlap <randy.dunlap@oracle.com>
Cc: Christoph Lameter <clameter@sgi.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent abb49202
...@@ -218,10 +218,14 @@ enum { ...@@ -218,10 +218,14 @@ enum {
DUMP_PREFIX_ADDRESS, DUMP_PREFIX_ADDRESS,
DUMP_PREFIX_OFFSET DUMP_PREFIX_OFFSET
}; };
extern void hex_dump_to_buffer(const void *buf, size_t len, char *linebuf, extern void hex_dump_to_buffer(const void *buf, size_t len,
size_t linebuflen); int rowsize, int groupsize,
extern void print_hex_dump(const char *level, int prefix_type, char *linebuf, size_t linebuflen, bool ascii);
void *buf, size_t len); extern void print_hex_dump(const char *level, const char *prefix_str,
int prefix_type, int rowsize, int groupsize,
void *buf, size_t len, bool ascii);
extern void print_hex_dump_bytes(const char *prefix_str, int prefix_type,
void *buf, size_t len);
#define hex_asc(x) "0123456789abcdef"[x] #define hex_asc(x) "0123456789abcdef"[x]
#ifdef DEBUG #ifdef DEBUG
......
...@@ -16,42 +16,98 @@ ...@@ -16,42 +16,98 @@
* hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
* @buf: data blob to dump * @buf: data blob to dump
* @len: number of bytes in the @buf * @len: number of bytes in the @buf
* @rowsize: number of bytes to print per line; must be 16 or 32
* @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
* @linebuf: where to put the converted data * @linebuf: where to put the converted data
* @linebuflen: total size of @linebuf, including space for terminating NUL * @linebuflen: total size of @linebuf, including space for terminating NUL
* @ascii: include ASCII after the hex output
* *
* hex_dump_to_buffer() works on one "line" of output at a time, i.e., * hex_dump_to_buffer() works on one "line" of output at a time, i.e.,
* 16 bytes of input data converted to hex + ASCII output. * 16 or 32 bytes of input data converted to hex + ASCII output.
* *
* Given a buffer of u8 data, hex_dump_to_buffer() converts the input data * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data
* to a hex + ASCII dump at the supplied memory location. * to a hex + ASCII dump at the supplied memory location.
* The converted output is always NUL-terminated. * The converted output is always NUL-terminated.
* *
* E.g.: * E.g.:
* hex_dump_to_buffer(frame->data, frame->len, linebuf, sizeof(linebuf)); * hex_dump_to_buffer(frame->data, frame->len, 16, 1,
* linebuf, sizeof(linebuf), 1);
* *
* example output buffer: * example output buffer:
* 40414243 44454647 48494a4b 4c4d4e4f @ABCDEFGHIJKLMNO * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
*/ */
void hex_dump_to_buffer(const void *buf, size_t len, char *linebuf, void hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
size_t linebuflen) int groupsize, char *linebuf, size_t linebuflen,
bool ascii)
{ {
const u8 *ptr = buf; const u8 *ptr = buf;
u8 ch; u8 ch;
int j, lx = 0; int j, lx = 0;
int ascii_column;
for (j = 0; (j < 16) && (j < len) && (lx + 3) < linebuflen; j++) { if (rowsize != 16 && rowsize != 32)
if (j && !(j % 4)) rowsize = 16;
if (!len)
goto nil;
if (len > rowsize) /* limit to one line at a time */
len = rowsize;
if ((len % groupsize) != 0) /* no mixed size output */
groupsize = 1;
switch (groupsize) {
case 8: {
const u64 *ptr8 = buf;
int ngroups = len / groupsize;
for (j = 0; j < ngroups; j++)
lx += scnprintf(linebuf + lx, linebuflen - lx,
"%16.16llx ", (unsigned long long)*(ptr8 + j));
ascii_column = 17 * ngroups + 2;
break;
}
case 4: {
const u32 *ptr4 = buf;
int ngroups = len / groupsize;
for (j = 0; j < ngroups; j++)
lx += scnprintf(linebuf + lx, linebuflen - lx,
"%8.8x ", *(ptr4 + j));
ascii_column = 9 * ngroups + 2;
break;
}
case 2: {
const u16 *ptr2 = buf;
int ngroups = len / groupsize;
for (j = 0; j < ngroups; j++)
lx += scnprintf(linebuf + lx, linebuflen - lx,
"%4.4x ", *(ptr2 + j));
ascii_column = 5 * ngroups + 2;
break;
}
default:
for (j = 0; (j < rowsize) && (j < len) && (lx + 4) < linebuflen;
j++) {
ch = ptr[j];
linebuf[lx++] = hex_asc(ch >> 4);
linebuf[lx++] = hex_asc(ch & 0x0f);
linebuf[lx++] = ' '; linebuf[lx++] = ' ';
ch = ptr[j]; }
linebuf[lx++] = hex_asc(ch >> 4); ascii_column = 3 * rowsize + 2;
linebuf[lx++] = hex_asc(ch & 0x0f); break;
} }
if ((lx + 2) < linebuflen) { if (!ascii)
linebuf[lx++] = ' '; goto nil;
while (lx < (linebuflen - 1) && lx < (ascii_column - 1))
linebuf[lx++] = ' '; linebuf[lx++] = ' ';
} for (j = 0; (j < rowsize) && (j < len) && (lx + 2) < linebuflen; j++)
for (j = 0; (j < 16) && (j < len) && (lx + 2) < linebuflen; j++)
linebuf[lx++] = isprint(ptr[j]) ? ptr[j] : '.'; linebuf[lx++] = isprint(ptr[j]) ? ptr[j] : '.';
nil:
linebuf[lx++] = '\0'; linebuf[lx++] = '\0';
} }
EXPORT_SYMBOL(hex_dump_to_buffer); EXPORT_SYMBOL(hex_dump_to_buffer);
...@@ -59,46 +115,83 @@ EXPORT_SYMBOL(hex_dump_to_buffer); ...@@ -59,46 +115,83 @@ EXPORT_SYMBOL(hex_dump_to_buffer);
/** /**
* print_hex_dump - print a text hex dump to syslog for a binary blob of data * print_hex_dump - print a text hex dump to syslog for a binary blob of data
* @level: kernel log level (e.g. KERN_DEBUG) * @level: kernel log level (e.g. KERN_DEBUG)
* @prefix_str: string to prefix each line with;
* caller supplies trailing spaces for alignment if desired
* @prefix_type: controls whether prefix of an offset, address, or none * @prefix_type: controls whether prefix of an offset, address, or none
* is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE) * is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
* @rowsize: number of bytes to print per line; must be 16 or 32
* @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
* @buf: data blob to dump * @buf: data blob to dump
* @len: number of bytes in the @buf * @len: number of bytes in the @buf
* @ascii: include ASCII after the hex output
* *
* Given a buffer of u8 data, print_hex_dump() prints a hex + ASCII dump * Given a buffer of u8 data, print_hex_dump() prints a hex + ASCII dump
* to the kernel log at the specified kernel log level, with an optional * to the kernel log at the specified kernel log level, with an optional
* leading prefix. * leading prefix.
* *
* print_hex_dump() works on one "line" of output at a time, i.e.,
* 16 or 32 bytes of input data converted to hex + ASCII output.
* print_hex_dump() iterates over the entire input @buf, breaking it into
* "line size" chunks to format and print.
*
* E.g.: * E.g.:
* print_hex_dump(KERN_DEBUG, DUMP_PREFIX_ADDRESS, frame->data, frame->len); * print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS,
* 16, 1, frame->data, frame->len, 1);
* *
* Example output using %DUMP_PREFIX_OFFSET: * Example output using %DUMP_PREFIX_OFFSET and 1-byte mode:
* 0009ab42: 40414243 44454647 48494a4b 4c4d4e4f @ABCDEFGHIJKLMNO * 0009ab42: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
* Example output using %DUMP_PREFIX_ADDRESS: * Example output using %DUMP_PREFIX_ADDRESS and 4-byte mode:
* ffffffff88089af0: 70717273 74757677 78797a7b 7c7d7e7f pqrstuvwxyz{|}~. * ffffffff88089af0: 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.
*/ */
void print_hex_dump(const char *level, int prefix_type, void *buf, size_t len) void print_hex_dump(const char *level, const char *prefix_str, int prefix_type,
int rowsize, int groupsize,
void *buf, size_t len, bool ascii)
{ {
u8 *ptr = buf; u8 *ptr = buf;
int i, linelen, remaining = len; int i, linelen, remaining = len;
unsigned char linebuf[100]; unsigned char linebuf[200];
for (i = 0; i < len; i += 16) { if (rowsize != 16 && rowsize != 32)
linelen = min(remaining, 16); rowsize = 16;
remaining -= 16;
hex_dump_to_buffer(ptr + i, linelen, linebuf, sizeof(linebuf)); for (i = 0; i < len; i += rowsize) {
linelen = min(remaining, rowsize);
remaining -= rowsize;
hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
linebuf, sizeof(linebuf), ascii);
switch (prefix_type) { switch (prefix_type) {
case DUMP_PREFIX_ADDRESS: case DUMP_PREFIX_ADDRESS:
printk("%s%*p: %s\n", level, printk("%s%s%*p: %s\n", level, prefix_str,
(int)(2 * sizeof(void *)), ptr + i, linebuf); (int)(2 * sizeof(void *)), ptr + i, linebuf);
break; break;
case DUMP_PREFIX_OFFSET: case DUMP_PREFIX_OFFSET:
printk("%s%.8x: %s\n", level, i, linebuf); printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf);
break; break;
default: default:
printk("%s%s\n", level, linebuf); printk("%s%s%s\n", level, prefix_str, linebuf);
break; break;
} }
} }
} }
EXPORT_SYMBOL(print_hex_dump); EXPORT_SYMBOL(print_hex_dump);
/**
* print_hex_dump_bytes - shorthand form of print_hex_dump() with default params
* @prefix_str: string to prefix each line with;
* caller supplies trailing spaces for alignment if desired
* @prefix_type: controls whether prefix of an offset, address, or none
* is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
* @buf: data blob to dump
* @len: number of bytes in the @buf
*
* Calls print_hex_dump(), with log level of KERN_DEBUG,
* rowsize of 16, groupsize of 1, and ASCII output included.
*/
void print_hex_dump_bytes(const char *prefix_str, int prefix_type,
void *buf, size_t len)
{
print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, 16, 1,
buf, len, 1);
}
EXPORT_SYMBOL(print_hex_dump_bytes);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment