LCOV - code coverage report
Current view: top level - common - hexdump.c (source / functions) Hit Total Coverage
Test: a simple test Lines: 53 78 67.9 %
Date: 2024-06-05 20:10:43 Functions: 2 2 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-only
       2             : /*
       3             :  * lib/hexdump.c
       4             :  */
       5             : 
       6             : #include <stdio.h>
       7             : 
       8             : #include "linux_types.h"
       9             : #include "defs.h"
      10             : 
      11             : #define __get_unaligned_t(type, ptr) ({                                    \
      12             :         const struct { type x; } __packed *__pptr = (typeof(__pptr))(ptr); \
      13             :         __pptr->x;                                                           \
      14             : })
      15             : 
      16             : #define get_unaligned(ptr)      __get_unaligned_t(typeof(*(ptr)), (ptr))
      17             : 
      18             : const char hex_asc[] = "0123456789abcdef";
      19             : 
      20             : #define hex_asc_lo(x)   hex_asc[((x) & 0x0f)]
      21             : #define hex_asc_hi(x)   hex_asc[((x) & 0xf0) >> 4]
      22             : 
      23             : void print_hex_dump(const char *prefix_str, int prefix_type,
      24             :                     int rowsize, int groupsize,
      25             :                     const void *buf, size_t len, bool ascii);
      26             : /**
      27             :  * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
      28             :  * @buf: data blob to dump
      29             :  * @len: number of bytes in the @buf
      30             :  * @rowsize: number of bytes to print per line; must be 16 or 32
      31             :  * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
      32             :  * @linebuf: where to put the converted data
      33             :  * @linebuflen: total size of @linebuf, including space for terminating NUL
      34             :  * @ascii: include ASCII after the hex output
      35             :  *
      36             :  * hex_dump_to_buffer() works on one "line" of output at a time, i.e.,
      37             :  * 16 or 32 bytes of input data converted to hex + ASCII output.
      38             :  *
      39             :  * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data
      40             :  * to a hex + ASCII dump at the supplied memory location.
      41             :  * The converted output is always NUL-terminated.
      42             :  *
      43             :  * E.g.:
      44             :  *   hex_dump_to_buffer(frame->data, frame->len, 16, 1,
      45             :  *                      linebuf, sizeof(linebuf), true);
      46             :  *
      47             :  * example output buffer:
      48             :  * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO
      49             :  *
      50             :  * Return:
      51             :  * The amount of bytes placed in the buffer without terminating NUL. If the
      52             :  * output was truncated, then the return value is the number of bytes
      53             :  * (excluding the terminating NUL) which would have been written to the final
      54             :  * string if enough space had been available.
      55             :  */
      56      829680 : static int hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
      57             :                               int groupsize, char *linebuf, size_t linebuflen,
      58             :                               bool ascii)
      59             : {
      60      829680 :         const u8 *ptr = buf;
      61             :         int ngroups;
      62             :         u8 ch;
      63      829680 :         int j, lx = 0;
      64             :         int ascii_column;
      65             :         int ret;
      66             : 
      67      829680 :         if (rowsize != 16 && rowsize != 32)
      68           0 :                 rowsize = 16;
      69             : 
      70      829680 :         if (len > rowsize)           /* limit to one line at a time */
      71           0 :                 len = rowsize;
      72     1659360 :         if (!is_power_of_2(groupsize) || groupsize > 8)
      73           0 :                 groupsize = 1;
      74      829680 :         if ((len % groupsize) != 0)     /* no mixed size output */
      75           0 :                 groupsize = 1;
      76             : 
      77      829680 :         ngroups = len / groupsize;
      78      829680 :         ascii_column = rowsize * 2 + rowsize / groupsize + 1;
      79             : 
      80      829680 :         if (!linebuflen)
      81             :                 goto overflow1;
      82             : 
      83      829680 :         if (!len)
      84             :                 goto nil;
      85             : 
      86      829680 :         if (groupsize == 8) {
      87             :                 const u64 *ptr8 = buf;
      88             : 
      89           0 :                 for (j = 0; j < ngroups; j++) {
      90           0 :                         ret = snprintf(linebuf + lx, linebuflen - lx,
      91             :                                        "%s%16.16llx", j ? " " : "",
      92           0 :                                        get_unaligned(ptr8 + j));
      93           0 :                         if (ret >= linebuflen - lx)
      94             :                                 goto overflow1;
      95           0 :                         lx += ret;
      96             :                 }
      97      829680 :         } else if (groupsize == 4) {
      98             :                 const u32 *ptr4 = buf;
      99             : 
     100     5136524 :                 for (j = 0; j < ngroups; j++) {
     101     5136524 :                         ret = snprintf(linebuf + lx, linebuflen - lx,
     102             :                                        "%s%8.8x", j ? " " : "",
     103     5136524 :                                        get_unaligned(ptr4 + j));
     104     5136524 :                         if (ret >= linebuflen - lx)
     105             :                                 goto overflow1;
     106     5136524 :                         lx += ret;
     107             :                 }
     108      187430 :         } else if (groupsize == 2) {
     109             :                 const u16 *ptr2 = buf;
     110             : 
     111           0 :                 for (j = 0; j < ngroups; j++) {
     112           0 :                         ret = snprintf(linebuf + lx, linebuflen - lx,
     113             :                                        "%s%4.4x", j ? " " : "",
     114           0 :                                        get_unaligned(ptr2 + j));
     115           0 :                         if (ret >= linebuflen - lx)
     116             :                                 goto overflow1;
     117           0 :                         lx += ret;
     118             :                 }
     119             :         } else {
     120     4513992 :                 for (j = 0; j < len; j++) {
     121     4513992 :                         if (linebuflen < lx + 2)
     122             :                                 goto overflow2;
     123     4513992 :                         ch = ptr[j];
     124     4513992 :                         linebuf[lx++] = hex_asc_hi(ch);
     125     4513992 :                         if (linebuflen < lx + 2)
     126             :                                 goto overflow2;
     127     4513992 :                         linebuf[lx++] = hex_asc_lo(ch);
     128     4513992 :                         if (linebuflen < lx + 2)
     129             :                                 goto overflow2;
     130     4513992 :                         linebuf[lx++] = ' ';
     131             :                 }
     132      187430 :                 if (j)
     133      187430 :                         lx--;
     134             :         }
     135      829680 :         if (!ascii)
     136             :                 goto nil;
     137             : 
     138     6939840 :         while (lx < ascii_column) {
     139     6112412 :                 if (linebuflen < lx + 2)
     140             :                         goto overflow2;
     141     6112412 :                 linebuf[lx++] = ' ';
     142             :         }
     143    24990368 :         for (j = 0; j < len; j++) {
     144    24990368 :                 if (linebuflen < lx + 2)
     145             :                         goto overflow2;
     146    24990368 :                 ch = ptr[j];
     147    24990368 :                 linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
     148             :         }
     149      829680 : nil:
     150      829680 :         linebuf[lx] = '\0';
     151      829680 :         return lx;
     152           0 : overflow2:
     153           0 :         linebuf[lx++] = '\0';
     154           0 : overflow1:
     155           0 :         return ascii ? ascii_column + len : (groupsize * 2 + 1) * ngroups - 1;
     156             : }
     157             : 
     158             : /**
     159             :  * print_hex_dump - print a text hex dump to syslog for a binary blob of data
     160             :  * @prefix_str: string to prefix each line with;
     161             :  *  caller supplies trailing spaces for alignment if desired
     162             :  * @prefix_type: controls whether prefix of an offset, address, or none
     163             :  *  is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
     164             :  * @rowsize: number of bytes to print per line; must be 16 or 32
     165             :  * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
     166             :  * @buf: data blob to dump
     167             :  * @len: number of bytes in the @buf
     168             :  * @ascii: include ASCII after the hex output
     169             :  *
     170             :  * Given a buffer of u8 data, print_hex_dump() prints a hex + ASCII dump
     171             :  * to the kernel log at the specified kernel log level, with an optional
     172             :  * leading prefix.
     173             :  *
     174             :  * print_hex_dump() works on one "line" of output at a time, i.e.,
     175             :  * 16 or 32 bytes of input data converted to hex + ASCII output.
     176             :  * print_hex_dump() iterates over the entire input @buf, breaking it into
     177             :  * "line size" chunks to format and print.
     178             :  *
     179             :  * E.g.:
     180             :  *   print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS,
     181             :  *                  16, 1, frame->data, frame->len, true);
     182             :  *
     183             :  * Example output using %DUMP_PREFIX_OFFSET and 1-byte mode:
     184             :  * 0009ab42: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO
     185             :  * Example output using %DUMP_PREFIX_ADDRESS and 4-byte mode:
     186             :  * ffffffff88089af0: 73727170 77767574 7b7a7978 7f7e7d7c  pqrstuvwxyz{|}~.
     187             :  */
     188      188162 : void print_hex_dump(const char *prefix_str, int prefix_type,
     189             :                     int rowsize, int groupsize,
     190             :                     const void *buf, size_t len, bool ascii)
     191             : {
     192      188162 :         const u8 *ptr = buf;
     193      188162 :         int i, linelen, remaining = len;
     194             :         char linebuf[32 * 3 + 2 + 32 + 1];
     195             : 
     196      188162 :         if (rowsize != 16 && rowsize != 32)
     197           0 :                 rowsize = 16;
     198             : 
     199     1017842 :         for (i = 0; i < len; i += rowsize) {
     200      829680 :                 linelen = min(remaining, rowsize);
     201      829680 :                 remaining -= rowsize;
     202             : 
     203      829680 :                 hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
     204             :                                    linebuf, sizeof(linebuf), ascii);
     205             : 
     206      829680 :                 switch (prefix_type) {
     207           0 :                 case DUMP_PREFIX_ADDRESS:
     208           0 :                         printf("%s%p: %s\n", prefix_str, ptr + i, linebuf);
     209           0 :                         break;
     210      829680 :                 case DUMP_PREFIX_OFFSET:
     211      829680 :                         printf("%s%.8x: %s\n", prefix_str, i, linebuf);
     212      829680 :                         break;
     213           0 :                 default:
     214           0 :                         printf("%s%s\n", prefix_str, linebuf);
     215           0 :                         break;
     216             :                 }
     217             :         }
     218      188162 : }

Generated by: LCOV version 1.13