hexdump.c 6.1 KB
Newer Older
R
Randy Dunlap 已提交
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);

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

R
Randy Dunlap 已提交
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 98
	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];
99 100
			linebuf[lx++] = hex_asc_hi(ch);
			linebuf[lx++] = hex_asc_lo(ch);
R
Randy Dunlap 已提交
101
			linebuf[lx++] = ' ';
R
Randy Dunlap 已提交
102 103 104
		}
		ascii_column = 3 * rowsize + 2;
		break;
R
Randy Dunlap 已提交
105
	}
R
Randy Dunlap 已提交
106 107 108 109
	if (!ascii)
		goto nil;

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

R
Randy Dunlap 已提交
158 159 160 161 162 163 164 165
	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);
R
Randy Dunlap 已提交
166 167 168

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

/**
 * 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,
196
			const void *buf, size_t len)
R
Randy Dunlap 已提交
197 198 199 200 201
{
	print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, 16, 1,
			buf, len, 1);
}
EXPORT_SYMBOL(print_hex_dump_bytes);