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.
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 details to configure and use the logging system are explained in the following sections.
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.
The following logging levels are defined from highest to lowest:
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 settings. 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).
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.
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:
Multiple domains can be concatenated using commas or semicolons. The match can be inverted by prefixing the domain(s) list with an exclamation mark.
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:
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.
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”
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 | |
---|---|
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
You can debug using the Visual Studio Debugger or WinDbg. See the section on using the Debugger Tools.