hexdump.c 6 KB
Newer Older
Randy Dunlap's avatar
Randy Dunlap committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * lib/hexdump.c
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation. See README and COPYING for
 * more details.
 */

#include <linux/types.h>
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/module.h>

/**
 * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
 * @buf: data blob to dump
 * @len: number of bytes in the @buf
Randy Dunlap's avatar
Randy Dunlap committed
19 20
 * @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)
Randy Dunlap's avatar
Randy Dunlap committed
21 22
 * @linebuf: where to put the converted data
 * @linebuflen: total size of @linebuf, including space for terminating NUL
Randy Dunlap's avatar
Randy Dunlap committed
23
 * @ascii: include ASCII after the hex output
Randy Dunlap's avatar
Randy Dunlap committed
24 25
 *
 * hex_dump_to_buffer() works on one "line" of output at a time, i.e.,
Randy Dunlap's avatar
Randy Dunlap committed
26
 * 16 or 32 bytes of input data converted to hex + ASCII output.
Randy Dunlap's avatar
Randy Dunlap committed
27 28 29 30 31 32
 *
 * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data
 * to a hex + ASCII dump at the supplied memory location.
 * The converted output is always NUL-terminated.
 *
 * E.g.:
Randy Dunlap's avatar
Randy Dunlap committed
33 34
 *   hex_dump_to_buffer(frame->data, frame->len, 16, 1,
 *			linebuf, sizeof(linebuf), 1);
Randy Dunlap's avatar
Randy Dunlap committed
35 36
 *
 * example output buffer:
Randy Dunlap's avatar
Randy Dunlap committed
37
 * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO
Randy Dunlap's avatar
Randy Dunlap committed
38
 */
Randy Dunlap's avatar
Randy Dunlap committed
39 40 41
void hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
			int groupsize, char *linebuf, size_t linebuflen,
			bool ascii)
Randy Dunlap's avatar
Randy Dunlap committed
42 43 44 45
{
	const u8 *ptr = buf;
	u8 ch;
	int j, lx = 0;
Randy Dunlap's avatar
Randy Dunlap committed
46
	int ascii_column;
Randy Dunlap's avatar
Randy Dunlap committed
47

Randy Dunlap's avatar
Randy Dunlap committed
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
	if (rowsize != 16 && rowsize != 32)
		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);
Randy Dunlap's avatar
Randy Dunlap committed
98
			linebuf[lx++] = ' ';
Randy Dunlap's avatar
Randy Dunlap committed
99 100 101
		}
		ascii_column = 3 * rowsize + 2;
		break;
Randy Dunlap's avatar
Randy Dunlap committed
102
	}
Randy Dunlap's avatar
Randy Dunlap committed
103 104 105 106
	if (!ascii)
		goto nil;

	while (lx < (linebuflen - 1) && lx < (ascii_column - 1))
Randy Dunlap's avatar
Randy Dunlap committed
107
		linebuf[lx++] = ' ';
Randy Dunlap's avatar
Randy Dunlap committed
108
	for (j = 0; (j < rowsize) && (j < len) && (lx + 2) < linebuflen; j++)
109 110
		linebuf[lx++] = (isascii(ptr[j]) && isprint(ptr[j])) ? ptr[j]
				: '.';
Randy Dunlap's avatar
Randy Dunlap committed
111
nil:
Randy Dunlap's avatar
Randy Dunlap committed
112 113 114 115 116 117 118
	linebuf[lx++] = '\0';
}
EXPORT_SYMBOL(hex_dump_to_buffer);

/**
 * print_hex_dump - print a text hex dump to syslog for a binary blob of data
 * @level: kernel log level (e.g. KERN_DEBUG)
Randy Dunlap's avatar
Randy Dunlap committed
119 120
 * @prefix_str: string to prefix each line with;
 *  caller supplies trailing spaces for alignment if desired
Randy Dunlap's avatar
Randy Dunlap committed
121 122
 * @prefix_type: controls whether prefix of an offset, address, or none
 *  is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
Randy Dunlap's avatar
Randy Dunlap committed
123 124
 * @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)
Randy Dunlap's avatar
Randy Dunlap committed
125 126
 * @buf: data blob to dump
 * @len: number of bytes in the @buf
Randy Dunlap's avatar
Randy Dunlap committed
127
 * @ascii: include ASCII after the hex output
Randy Dunlap's avatar
Randy Dunlap committed
128 129 130 131 132
 *
 * 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
 * leading prefix.
 *
Randy Dunlap's avatar
Randy Dunlap committed
133 134 135 136 137
 * 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.
 *
Randy Dunlap's avatar
Randy Dunlap committed
138
 * E.g.:
Randy Dunlap's avatar
Randy Dunlap committed
139 140
 *   print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS,
 *		16, 1, frame->data, frame->len, 1);
Randy Dunlap's avatar
Randy Dunlap committed
141
 *
Randy Dunlap's avatar
Randy Dunlap committed
142 143 144 145
 * Example output using %DUMP_PREFIX_OFFSET and 1-byte mode:
 * 0009ab42: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO
 * Example output using %DUMP_PREFIX_ADDRESS and 4-byte mode:
 * ffffffff88089af0: 73727170 77767574 7b7a7978 7f7e7d7c  pqrstuvwxyz{|}~.
Randy Dunlap's avatar
Randy Dunlap committed
146
 */
Randy Dunlap's avatar
Randy Dunlap committed
147 148
void print_hex_dump(const char *level, const char *prefix_str, int prefix_type,
			int rowsize, int groupsize,
Artem Bityutskiy's avatar
Artem Bityutskiy committed
149
			const void *buf, size_t len, bool ascii)
Randy Dunlap's avatar
Randy Dunlap committed
150
{
Artem Bityutskiy's avatar
Artem Bityutskiy committed
151
	const u8 *ptr = buf;
Randy Dunlap's avatar
Randy Dunlap committed
152
	int i, linelen, remaining = len;
Randy Dunlap's avatar
Randy Dunlap committed
153
	unsigned char linebuf[200];
Randy Dunlap's avatar
Randy Dunlap committed
154

Randy Dunlap's avatar
Randy Dunlap committed
155 156 157 158 159 160 161 162
	if (rowsize != 16 && rowsize != 32)
		rowsize = 16;

	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);
Randy Dunlap's avatar
Randy Dunlap committed
163 164 165

		switch (prefix_type) {
		case DUMP_PREFIX_ADDRESS:
Randy Dunlap's avatar
Randy Dunlap committed
166
			printk("%s%s%*p: %s\n", level, prefix_str,
Randy Dunlap's avatar
Randy Dunlap committed
167 168 169
				(int)(2 * sizeof(void *)), ptr + i, linebuf);
			break;
		case DUMP_PREFIX_OFFSET:
Randy Dunlap's avatar
Randy Dunlap committed
170
			printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf);
Randy Dunlap's avatar
Randy Dunlap committed
171 172
			break;
		default:
Randy Dunlap's avatar
Randy Dunlap committed
173
			printk("%s%s%s\n", level, prefix_str, linebuf);
Randy Dunlap's avatar
Randy Dunlap committed
174 175 176 177 178
			break;
		}
	}
}
EXPORT_SYMBOL(print_hex_dump);
Randy Dunlap's avatar
Randy Dunlap committed
179 180 181 182 183 184 185 186 187 188 189 190 191 192

/**
 * 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,
193
			const void *buf, size_t len)
Randy Dunlap's avatar
Randy Dunlap committed
194 195 196 197 198
{
	print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, 16, 1,
			buf, len, 1);
}
EXPORT_SYMBOL(print_hex_dump_bytes);