# How to Debug ASIO Applications Effectively: Handler Tracking and Diagnostic Tools

> Effectively debug ASIO applications with handler tracking, buffer validation, and kernel socket diagnostics. Learn to trace asynchronous handler lifecycles and prevent common errors.

- Repository: [chriskohlhoff/asio](https://github.com/chriskohlhoff/asio)
- Tags: tutorial
- Published: 2026-07-12

---

**Define `ASIO_ENABLE_HANDLER_TRACKING` to trace asynchronous handler lifecycles, enable `ASIO_ENABLE_BUFFER_DEBUG` to validate buffer boundaries, and use `asio::socket_base::debug` to activate kernel-level socket diagnostics.**

Debugging asynchronous I/O in the chriskohlhoff/asio library requires visibility into handler execution and memory management. By leveraging compile-time macros and diagnostic APIs implemented in the ASIO headers, you can inspect the event loop, track buffer usage, and identify socket errors without modifying your core application logic.

## Enable Handler Tracking to Expose Asynchronous Flows

When you define `ASIO_ENABLE_HANDLER_TRACKING`, ASIO injects instrumentation code that records every handler creation, invocation, and destruction. This mechanism lives in [`include/asio/detail/handler_tracking.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/detail/handler_tracking.hpp) and creates a chronological log of asynchronous operations.

To view the tracking data, call `asio::detail::handler_tracking::dump()` before your program exits. This outputs handler IDs and timestamps to `std::cerr`, revealing exactly which callback executed and when.

```cpp
#define ASIO_ENABLE_HANDLER_TRACKING 1
#include <asio.hpp>
#include <iostream>

void on_read(const asio::error_code& ec, std::size_t bytes) {
  std::cout << "Read " << bytes << " bytes\n";
}

int main() {
  asio::io_context ctx;
  asio::ip::tcp::socket sock(ctx);
  
  // ... setup and connect socket ...
  
  char data[128];
  sock.async_read_some(asio::buffer(data), on_read);
  ctx.run();
  
  // Dump handler tracking information
  asio::detail::handler_tracking::dump(std::cerr);
}

```

## Activate Buffer Debug Checks to Catch Memory Errors

Define `ASIO_ENABLE_BUFFER_DEBUG` to enable runtime validation of buffer boundaries on every read and write operation. This activates the `asio::detail::buffer_debug_check` utilities located in [`include/asio/buffer.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/buffer.hpp), which verify that buffer iterators and sizes remain valid throughout the operation.

Compile your application with this macro to catch buffer overruns and invalid memory accesses during development:

```bash
g++ -std=c++17 -DASIO_ENABLE_BUFFER_DEBUG=1 -DASIO_ENABLE_HANDLER_TRACKING=1 \
    main.cpp -I/path/to/asio/include -lpthread

```

## Use Socket-Level Debug Options for Kernel Diagnostics

ASIO provides the `asio::socket_base::debug` option (defined in [`include/asio/socket_base.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/socket_base.hpp)) to enable `SO_DEBUG` on the underlying socket. When set to `true`, this requests the operating system kernel to produce debugging output for the socket, which typically appears in system logs or kernel debug buffers.

```cpp
#include <asio.hpp>

int main() {
  asio::io_context ctx;
  asio::ip::tcp::socket s(ctx);
  
  // Enable kernel-level socket debugging
  asio::socket_base::debug dbg(true);
  s.set_option(dbg);
  
  // ... proceed with async operations ...
}

```

## Trace Handler Memory Allocations

To diagnose memory usage, implement a custom allocator that logs allocations and deallocations. The ASIO repository provides a reference implementation in [`src/tests/performance/handler_allocator.hpp`](https://github.com/chriskohlhoff/asio/blob/main/src/tests/performance/handler_allocator.hpp), which demonstrates how to override `asio_handler_allocate` and `asio_handler_deallocate`.

Use `asio::bind_allocator()` to attach your logging allocator to specific handlers:

```cpp
#include <asio.hpp>
#include <iostream>

struct logging_alloc {
  static void* allocate(std::size_t n) {
    std::cerr << "[alloc] " << n << " bytes\n";
    return ::operator new(n);
  }
  
  static void deallocate(void* p, std::size_t) {
    std::cerr << "[dealloc]\n";
    ::operator delete(p);
  }
};

void handler(const asio::error_code&) { /* ... */ }

int main() {
  asio::io_context ctx;
  
  // Bind custom allocator to handler
  auto logged_handler = asio::bind_allocator(logging_alloc{}, handler);
  
  // Use logged_handler in async operations
  // socket.async_read_some(buffer, logged_handler);
}

```

## Step-by-Step Debugging Workflow

Follow this systematic approach to diagnose issues in ASIO applications:

1. **Compile with diagnostic macros**  
   Add `-DASIO_ENABLE_HANDLER_TRACKING=1` and `-DASIO_ENABLE_BUFFER_DEBUG=1` to your compiler flags to enable runtime diagnostics.

2. **Enable socket debugging**  
   Call `socket.set_option(asio::socket_base::debug(true))` for network-level issues to capture kernel diagnostics.

3. **Instrument memory allocation**  
   Replace default allocators with logging versions using `asio::bind_allocator()` to track handler memory usage.

4. **Analyze handler tracking output**  
   Call `asio::detail::handler_tracking::dump(std::cerr)` after `io_context::run()` returns to review the asynchronous execution sequence.

5. **Run under a debugger**  
   Set breakpoints in your handler's `operator()` and use the handler IDs from the tracking output to correlate specific callbacks with call stacks.

## Summary

- **Handler tracking** (`ASIO_ENABLE_HANDLER_TRACKING`) exposes the lifecycle of every asynchronous callback through [`include/asio/detail/handler_tracking.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/detail/handler_tracking.hpp).
- **Buffer debug checks** (`ASIO_ENABLE_BUFFER_DEBUG`) validate memory boundaries via `asio::detail::buffer_debug_check` in [`include/asio/buffer.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/buffer.hpp).
- **Socket debugging** uses `asio::socket_base::debug` from [`include/asio/socket_base.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/socket_base.hpp) to enable kernel-level `SO_DEBUG`.
- **Custom allocators** based on [`src/tests/performance/handler_allocator.hpp`](https://github.com/chriskohlhoff/asio/blob/main/src/tests/performance/handler_allocator.hpp) let you trace memory usage with `asio::bind_allocator()`.
- **Diagnostic workflow** combines compile-time macros, runtime API calls, and standard debugging tools to reveal asynchronous execution patterns.

## Frequently Asked Questions

### What is ASIO handler tracking?

Handler tracking is a diagnostic feature that logs when asynchronous handlers are created, invoked, and destroyed. When you define `ASIO_ENABLE_HANDLER_TRACKING`, the library instruments your code via [`include/asio/detail/handler_tracking.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/detail/handler_tracking.hpp) to output a chronological trace of handler execution, which you can dump using `asio::detail::handler_tracking::dump()`.

### How does ASIO buffer debugging work?

Buffer debugging activates runtime checks in `asio::detail::buffer_debug_check` (found in [`include/asio/buffer.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/buffer.hpp)) when you compile with `ASIO_ENABLE_BUFFER_DEBUG`. These checks validate that buffer iterators and sizes remain valid during read and write operations, catching out-of-bounds access before they cause undefined behavior.

### Can I use handler tracking in production?

Handler tracking adds significant overhead by recording every handler operation and should be reserved for development and debugging. The instrumentation increases memory usage and impacts performance, so disable it in production builds by removing the `ASIO_ENABLE_HANDLER_TRACKING` definition.

### Where is the socket debug option defined?

The `debug` socket option is defined as `asio::socket_base::debug` in [`include/asio/socket_base.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/socket_base.hpp). Setting this option to `true` enables the `SO_DEBUG` socket option, which requests the operating system kernel to generate diagnostic output for that specific socket.