Today I want to share with you some code which helped me to track down a problem in a rather complex networking VoIP application. I believe it might be useful in other scenarios as well.

Some time ago I debugged a networked application. The problem seemed to be in the handling of one type of network packets and two versions of the application behaved differently under similar circumstances. There is one known issue with debugging networked applications - timing might be very important and, in some cases, there is no much time to diagnose raw data which was received via networking socket in a debugger. More than that, the format of the data was binary and, thus, not human readable. Using network sniffing software (like Wireshark) was not an option, as parts of the message contained encrypted information.

To verify my hypothesis without interfering much with the application during runtime, I decided to dump contents of the parts of the message to a log file in hexadecimal format and analyse the data later. The format closely resembles one, used by HEX-editors, as you will see. In fact, I adopted most of the source code from a HEX-viewing utility which I wrote as a university assignment a long time ago.

Here is the source code of a function, which dumps contents of a part of random access memory in hexadecimal format:

void dumpmem(FILE *out, const void *ptr, const size_t size)
{
    const size_t BYTES_PER_LINE = 16;
    size_t offset, read;

    uint8_t *p = (uint8_t *)ptr;
    const uint8_t *maxp = (p + size);

    if (out == NULL || ptr == NULL || size == 0)
    {
        return;
    }

    for (offset = read = 0; offset != size; offset += read)
    {
        uint8_t buf[BYTES_PER_LINE];

        for (read = 0; read != BYTES_PER_LINE && (&p[offset + read]) < maxp; read++)
        {
            buf[read] = p[offset + read];
        }

        if (read == 0)
            return;

        fprintf(out, "%.8x: ", (unsigned int)offset);

        /* raw data */
        for (size_t i = 0; i < read; i++)
        {
            fprintf(out, " %.2x", buf[i]);
            if (BYTES_PER_LINE > 8 && BYTES_PER_LINE % 2 == 0 && i == (BYTES_PER_LINE / 2 - 1))
                fprintf(out, " ");
        }

        /* ASCII */
        if (read < BYTES_PER_LINE)
        {
            for (size_t i = read; i < BYTES_PER_LINE; i++)
            {
                fprintf(out, "  ");
                fprintf(out, " ");
                if (BYTES_PER_LINE > 8 && BYTES_PER_LINE % 2 == 0 && i == (BYTES_PER_LINE / 2 - 1))
                    fprintf(out, " ");
            }
        }
        fprintf(out, " ");
        for (size_t i = 0; i < read; i++)
        {
            if (buf[i] <= 31 || buf[i] >= 127) /* ignore control and non-ASCII characters */
                fprintf(out, ".");
            else
                fprintf(out, "%c", buf[i]);
        }

        fprintf(out, "\n");
    }
}

As one could see, it accepts three parameters:

  1. out - output file stream;
  2. ptr - pointer to data;
  3. size - data size.

There is some room for optimisation, but I think that it is not worth it.

If you wonder how the output of this function looks like, here is a short example of an H.225 RAS message:

00000000:  03 00 00 ab 08 02 18 af  05 04 04 88 18 a0 a5 28 ...............(
00000010:  06 76 63 34 30 30 00 6c  04 81 36 30 31 7e 00 8b .vc400.l..601~..
00000020:  05 20 b8 06 00 08 91 4a  00 06 02 01 00 93 44 04 . .....J......D.
00000030:  00 76 00 63 00 34 00 30  00 30 22 c0 26 00 00 02 .v.c.4.0.0".&...
00000040:  0e 59 65 61 6c 69 6e 6b  20 56 43 34 30 30 00 00 .Yealink VC400..
00000050:  0b 33 30 2e 31 30 2e 30  2e 32 37 00 00 00 01 40 .30.10.0.27....@
00000060:  03 00 61 00 73 00 72 00  76 00 c0 a8 29 9c 06 b8 ..a.s.r.v...)...
00000070:  00 dc 27 ec f9 6b ef e5  11 94 0e 00 15 65 86 fc ..'..k.......e..
00000080:  9f 00 5d 0d 80 07 00 c0  a8 5a 0e 06 b8 11 00 82 ..]......Z......
00000090:  27 ec f9 6b ef e5 11 94  0e 00 15 65 86 fc 9f 01 '..k.......e....
000000a0:  00 01 00 01 00 01 00 02  80 01 00                ...........

Off course, the discussed debugging technique is not the panacea and should be used only in very specific cases, as, I believe, one could obtain similar results using modern debugging tools. But anyway it helped me to solve a problem in short time, so I decided to share this information here. It is not a tool for everyday use, but still, I think, it is a trick worth to know about.