hexdump.c 6.12 KB
Newer Older
Randy Dunlap's avatar
Randy Dunlap committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * 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>

15 16 17
const char hex_asc[] = "0123456789abcdef";
EXPORT_SYMBOL(hex_asc);

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

Randy Dunlap's avatar
Randy Dunlap committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
	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,
68 69
					"%s%16.16llx", j ? " " : "",
					(unsigned long long)*(ptr8 + j));
Randy Dunlap's avatar
Randy Dunlap committed
70 71 72 73 74 75 76 77 78 79
		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,
80
					"%s%8.8x", j ? " " : "", *(ptr4 + j));
Randy Dunlap's avatar
Randy Dunlap committed
81 82 83 84 85 86 87 88 89 90
		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,
91
					"%s%4.4x", j ? " " : "", *(ptr2 + j));
Randy Dunlap's avatar
Randy Dunlap committed
92 93 94 95 96
		ascii_column = 5 * ngroups + 2;
		break;
	}

	default:
Li Zefan's avatar
Li Zefan committed
97
		for (j = 0; (j < len) && (lx + 3) <= linebuflen; j++) {
Randy Dunlap's avatar
Randy Dunlap committed
98
			ch = ptr[j];
99 100
			linebuf[lx++] = hex_asc_hi(ch);
			linebuf[lx++] = hex_asc_lo(ch);
Randy Dunlap's avatar
Randy Dunlap committed
101
			linebuf[lx++] = ' ';
Randy Dunlap's avatar
Randy Dunlap committed
102
		}
Li Zefan's avatar
Li Zefan committed
103 104 105
		if (j)
			lx--;

Randy Dunlap's avatar
Randy Dunlap committed
106 107
		ascii_column = 3 * rowsize + 2;
		break;
Randy Dunlap's avatar
Randy Dunlap committed
108
	}
Randy Dunlap's avatar
Randy Dunlap committed
109 110 111 112
	if (!ascii)
		goto nil;

	while (lx < (linebuflen - 1) && lx < (ascii_column - 1))
Randy Dunlap's avatar
Randy Dunlap committed
113
		linebuf[lx++] = ' ';
114 115 116 117
	for (j = 0; (j < len) && (lx + 2) < linebuflen; j++) {
		ch = ptr[j];
		linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
	}
Randy Dunlap's avatar
Randy Dunlap committed
118
nil:
Randy Dunlap's avatar
Randy Dunlap committed
119 120 121 122 123 124 125
	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
126 127
 * @prefix_str: string to prefix each line with;
 *  caller supplies trailing spaces for alignment if desired
Randy Dunlap's avatar
Randy Dunlap committed
128 129
 * @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
130 131
 * @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
132 133
 * @buf: data blob to dump
 * @len: number of bytes in the @buf
Randy Dunlap's avatar
Randy Dunlap committed
134
 * @ascii: include ASCII after the hex output
Randy Dunlap's avatar
Randy Dunlap committed
135 136 137 138 139
 *
 * 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
140 141 142 143 144
 * 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
145
 * E.g.:
Randy Dunlap's avatar
Randy Dunlap committed
146
 *   print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS,
147
 *		    16, 1, frame->data, frame->len, true);
Randy Dunlap's avatar
Randy Dunlap committed
148
 *
Randy Dunlap's avatar
Randy Dunlap committed
149 150 151 152
 * 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
153
 */
Randy Dunlap's avatar
Randy Dunlap committed
154
void print_hex_dump(const char *level, const char *prefix_str, int prefix_type,
155 156
		    int rowsize, int groupsize,
		    const void *buf, size_t len, bool ascii)
Randy Dunlap's avatar
Randy Dunlap committed
157
{
Artem Bityutskiy's avatar
Artem Bityutskiy committed
158
	const u8 *ptr = buf;
Randy Dunlap's avatar
Randy Dunlap committed
159
	int i, linelen, remaining = len;
160
	unsigned char linebuf[32 * 3 + 2 + 32 + 1];
Randy Dunlap's avatar
Randy Dunlap committed
161

Randy Dunlap's avatar
Randy Dunlap committed
162 163 164 165 166 167
	if (rowsize != 16 && rowsize != 32)
		rowsize = 16;

	for (i = 0; i < len; i += rowsize) {
		linelen = min(remaining, rowsize);
		remaining -= rowsize;
168

Randy Dunlap's avatar
Randy Dunlap committed
169
		hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
170
				   linebuf, sizeof(linebuf), ascii);
Randy Dunlap's avatar
Randy Dunlap committed
171 172 173

		switch (prefix_type) {
		case DUMP_PREFIX_ADDRESS:
174 175
			printk("%s%s%p: %s\n",
			       level, prefix_str, ptr + i, linebuf);
Randy Dunlap's avatar
Randy Dunlap committed
176 177
			break;
		case DUMP_PREFIX_OFFSET:
Randy Dunlap's avatar
Randy Dunlap committed
178
			printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf);
Randy Dunlap's avatar
Randy Dunlap committed
179 180
			break;
		default:
Randy Dunlap's avatar
Randy Dunlap committed
181
			printk("%s%s%s\n", level, prefix_str, linebuf);
Randy Dunlap's avatar
Randy Dunlap committed
182 183 184 185 186
			break;
		}
	}
}
EXPORT_SYMBOL(print_hex_dump);
Randy Dunlap's avatar
Randy Dunlap committed
187 188 189 190 191 192 193 194 195 196 197 198 199 200

/**
 * 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,
201
			  const void *buf, size_t len)
Randy Dunlap's avatar
Randy Dunlap committed
202 203
{
	print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, 16, 1,
204
		       buf, len, true);
Randy Dunlap's avatar
Randy Dunlap committed
205 206
}
EXPORT_SYMBOL(print_hex_dump_bytes);