3.8. Debug Your Version Of Wireshark

Optimization can make debugging a bit more difficult, e.g. by changing the execution order of statements. To disable optimization, set the build type to Debug.

Full debug logs can be invaluable to investigate any issues with the code. By default debug level logs are only enabled with Debug build type. You can enable full debug logs and extra debugging code by configuring the ENABLE_DEBUG CMake option. This in turn will define the macro symbol WS_DEBUG and enable the full range of debugging code in Wireshark.

There is an additional CMake knob called ENABLE_DEBUG_UTF_8 that can be used to control specifically the extra validation Wireshark performs internally for invalid UTF-8 encodings in internal strings, which should never happen and can be somewhat expensive to check during normal usage.

Conversely, the Release or MinSizeRel build types can be used to optimize further for speed or size, but do not include debug symbols for use with debuggers, and completely disable lower level logging and asserts, optimizing away the code path. Ensure that you have not built with one of those types before attempting debugging.

3.8.1. Wireshark Logging

Wireshark has a flexible logging system to assist in development and troubleshooting. Logging configuration takes into account what, when and where to output diagnostic messages.

  • The 'what generates log messages' is defined through logging domain(s).
  • The 'when it generates log messages' is defined through the logging level.
  • The 'where it outputs log messages' is defined through the output channel(s).

The details to configure and use the logging system are explained in the following sections.

3.8.1.1. Logging Domains

Any part of Wireshark can be assigned a logging domain. This is already done for most of the internals of Wireshark, e.g., "Main", "Capture", "Epan", "GUI". The domains are defined in the ws_log_defs.h header but dissectors should define their own logging domain. Any string can be used as ID for a logging domain.

3.8.1.2. Logging Levels

The following logging levels are defined from highest to lowest:

  • error
  • critical
  • warning
  • message
  • info
  • debug
  • noisy

By default logging output is generated for logging level "message" and above. If the logging level is lowered or raised all log output generated at or above this level is sent to the log output. Note that if the build type is not set to Debug then by default all log output for the logging levels "debug" and "noisy" will be optimized away by the compiler and cannot be emitted to the log output, regardless of the logging setings. To enable debug logging for all build types, set the CMake variable -DENABLE_DEBUG=ON.

There is also a special "echo" logging level used exclusively for temporary debugging print outs (usually via the WS_DEBUG_HERE macro).

3.8.1.3. Logging Output

By default logging output is sent to stderr. In addition to that it is possible to configure a log file. This collects all log output to the file, besides the normal output streams. The output can then be read in a text editor or used with other text processing tools.

A program can also register its own log writer when the standard facilities are insufficient or special handling is required.

3.8.1.4. Configure Logging

Logging can be configured through either environment variables or command line parameters.

The following environment variables and command line parameters are used by the logging system:

WIRESHARK_LOG_DOMAIN, WIRESHARK_LOG_DOMAINS, or --log-domain, --log-domains
This is a filter for the domain(s) which are to generate log messages.
WIRESHARK_LOG_LEVEL, or --log-level
This is the level (below critical) for which log messages are to be generated. This is used for all configured domains.
WIRESHARK_LOG_DEBUG, or --log-debug
These domain(s) will generate debug level log messages regardless of the log level and log domains configured.
WIRESHARK_LOG_NOISY, or --log_noisy
These domain(s) will generate noisy level log messages regardless of the log level and log domains configured.

Multiple domains can be concatenated using commas or semicolons. The match can be inverted by prefixing the domain(s) list with an exclamation mark.

3.8.2. Traps Set By Logging

Sometimes it can be helpful to abort the program right after a log message of a certain level or a certain domain is output.

The following environment variables are used to configure a trap by the logging system:

WIRESHARK_LOG_FATAL, or --log_fatal
This is the level for which log messages are fatal. This can either be "critical" or "warning" level.
WIRESHARK_LOG_FATAL_DOMAIN, WIRESHARK_LOG_FATAL_DOMAINS, or --log-fatal-domain, --log-fatal-domains
These are the domain(s) where output of a log message is fatal. This is less commonly used than the fatal log level setting above.

3.8.3. Logging APIs

The logging API can be found in wsutil/wslog.h.

To use the logging API for your code add the definition of the ID of your logging domain right after including config.h. For example:

/* My code doing something awesome */
#include "config.h"
#define WS_LOG_DOMAIN "MyCode"

#include <wireshark.h>

...

Populate your code with the applicable function calls to generate log messages when enabled. The following convenience macros are provided:

  • ws_error()
  • ws_critical()
  • ws_warning()
  • ws_message()
  • ws_info()
  • ws_debug()
  • ws_noisy()

All these take printf() style parameters. There is also a WS_DEBUG_HERE macro that is always active and outputs to a special "echo" domain for temporary debug print outs. WS_DEBUG_HERE should be used for development purposes only and not appear in final delivery of the code.

3.8.4. Unix-Like Platforms

You can debug using command-line debuggers such as gdb, dbx, or lldb. If you prefer a graphic debugger, you can use an IDE or debugging frontend such as Qt Creator, CLion, or Eclipse.

Additional traps can be set on Wireshark, see Section 3.8.2, “Traps Set By Logging”

3.8.4.1. Memory Safety and Leaks

Wireshark’s wmem memory management framework makes it easy to allocate memory in pools with a certain scope that is freed automatically at a certain point (such as the end of dissecting a packet or when closing a file), even if a dissector raises an exception after allocating the memory. Memory in a pool is also freed collectively, which can be considerably faster than calling free() individually on each individual allocation. Proper use of wmem makes a dissector faster and less prone to memory leaks with unexpected data, which happens frequently with capture files.

However, wmem’s block allocation can obscure issues that memory checkers might otherwise catch. Fortunately, the WIRESHARK_DEBUG_WMEM_OVERRIDE environment variable can be set at runtime to instruct wmem to use a specific memory allocator for all allocations, some of which are more compatible with memory checkers:

  • simple - Uses malloc() only, no block allocation, compatible with Valgrind
  • strict - Finds invalid memory via canaries and scrubbing freed memory
  • block - Standard block allocator for file and epan scopes
  • block_fast - Block allocator for short-lived scope, e.g. packet, (free() is a no-op)

The simple allocator produces the most accurate results with tools like Valgrind and can be enabled as follows:

$ export WIRESHARK_DEBUG_WMEM_OVERRIDE=simple

Wireshark uses GLib’s GSlice memory allocator, either indirectly via wmem or via various GLib API calls. GLib provides a G_SLICE environment variable that can be set to always-malloc (similar to simple) or debug-blocks (similar to strict). See https://developer-old.gnome.org/glib/stable/glib-running.html for details. The C libraries on FreeBSD, Linux, and macOS also support memory allocation debugging via various environment variables. You can enable many of them by running source tools/debug-alloc.env in a POSIX shell.

If you’re encountering memory safety bugs, you might want to build with Address Sanitizer (ASAN) so that Wireshark will immediately alert you to any detected issues. It works with GCC or Clang, provided that the appropriate libraries are installed.

$ cmake .. -G Ninja -DENABLE_ASAN=1
$ source ../tools/debug-alloc.env
$ ./run/tshark ...
[Tip]Tip

ASAN slows things down by a factor of 2 (or more), so having a different build directory for an ASAN build can be useful.

ASAN will catch more errors when run with either the simple or strict wmem allocator than with the defaults. (It is more compatible with the strict allocator and the analogous GSlice debug-blocks option than Valgrind is.)

For additional instrumentation, ASAN supports a number of options.

For further investigating memory leaks, the following can be useful:

# This slows things down a lot more but results in more precise backtraces,
# especially when calling third party libraries (such as the C++ standard
# library):
$ export ASAN_OPTIONS=fast_unwind_on_malloc=0
# This causes LeakSanitizer to print the addresses of leaked objects for
# inspection in a debugger:
$ export LSAN_OPTIONS=report_objects=1

LeakSanitizer and AddressSanitizer can detect issues in third-party libraries that you cannot do anything about. For example, internal Qt library calls to the fontconfig library can produce leaks. To ignore them, create a suppressions file with an appropriate entry, e.g. leak:libfontconfig.

If you are just interested in memory safety checking, but not memory leak debugging, disable the included LeakSanitizer with:

$ export ASAN_OPTIONS=detect_leaks=0

3.8.5. Windows Native

You can debug using the Visual Studio Debugger or WinDbg. See the section on using the Debugger Tools.