# How to Monitor bannedbook/fanqiang Performance: Profiling the DNS Proxy and Go Core

> Learn to monitor bannedbook/fanqiang performance by profiling DNS proxy and Go core. Discover techniques for latency metrics, CPU/memory profiling, and UI thread stall analysis.

- Repository: [如何翻墙/fanqiang](https://github.com/bannedbook/fanqiang)
- Tags: performance
- Published: 2026-06-15

---

**Monitor bannedbook/fanqiang performance by instrumenting the ChannelMonitor class in LocalDnsServer.kt for DNS latency metrics, enabling pprof endpoints in the Go libcore for CPU and memory profiling, and using Android Studio Profiler to analyze UI thread stalls.**

The bannedbook/fanqiang repository implements a sophisticated circumvention VPN combining a Kotlin-based Android DNS proxy with a high-performance Go networking core. Understanding how to monitor bannedbook/fanqiang performance requires observing the non-blocking I/O operations in the ChannelMonitor selector loop and the packet processing routines in the Go libcore. This guide identifies the exact source file locations and instrumentation code needed to capture comprehensive latency, throughput, and resource utilization metrics.

## Instrumenting the DNS Proxy ChannelMonitor

The Android DNS proxy relies on [`LocalDnsServer.kt`](https://github.com/bannedbook/fanqiang/blob/main/LocalDnsServer.kt) and [`ChannelMonitor.kt`](https://github.com/bannedbook/fanqiang/blob/main/ChannelMonitor.kt) in `fqnews/core/src/main/java/com/github/shadowsocks/net/` to manage non-blocking NIO channel operations. The **ChannelMonitor** class runs a dedicated selector thread that processes registrations and readiness events, avoiding `NetworkOnMainThreadException` while handling DNS requests.

The `register()` method sends channel registration requests to a `pendingRegistrations` channel. By adding timestamp logging at the entry point of this method and when channels become ready, you can calculate per-operation latency. The class uses a `writeCompat` extension function to handle API compatibility for `WritableByteChannel` operations, and `printLog(e)` for error handling.

Add timestamp instrumentation to the `register()` method:

```kotlin
// ChannelMonitor.kt – add simple timestamp logging
private suspend fun WritableByteChannel.writeCompat(src: ByteBuffer) =
    if (Build.VERSION.SDK_INT <= 23) withContext(Dispatchers.Default) { write(src) } else write(src)

suspend fun register(channel: SelectableChannel, ops: Int, block: (SelectionKey) -> Unit): SelectionKey {
    val registration = Registration(channel, ops, block)
    pendingRegistrations.send(registration)
    println("[${System.currentTimeMillis()}] REGISTER ${channel} ops=$ops")
    ByteBuffer.allocateDirect(1).also { junk ->
        loop@ while (running) when (registrationPipe.sink().writeCompat(junk)) {
            0 -> kotlinx.coroutines.yield()
            1 -> break@loop
            else -> throw IOException("Failed to register in the channel")
        }
    }
    if (!running) throw CancellationException()
    return registration.result.await()
}

```

Similarly, instrument the `wait()` coroutine to emit completion timestamps when channels become ready for `OP_READ` or `OP_WRITE` operations.

## Profiling the Go Core with pprof

The Go networking components reside in `fqnews2/libcore`, including the STUN client implementation in [`stun/client.go`](https://github.com/bannedbook/fanqiang/blob/main/stun/client.go) and DNS handling in [`dns_box.go`](https://github.com/bannedbook/fanqiang/blob/main/dns_box.go). To monitor bannedbook/fanqiang performance at the network layer, integrate the Go `pprof` package to expose runtime metrics.

Add a pprof HTTP endpoint in [`fqnews2/libcore/http.go`](https://github.com/bannedbook/fanqiang/blob/main/fqnews2/libcore/http.go):

```go
package libcore

import (
    "log"
    "net/http"
    _ "net/http/pprof" // registers /debug/pprof/* handlers
)

func init() {
    go func() {
        log.Println("pprof listening on :6060")
        if err := http.ListenAndServe(":6060", nil); err != nil {
            log.Fatalf("pprof failed: %v", err)
        }
    }()
}

```

With this endpoint active, capture CPU profiles while the VPN processes traffic:

```bash
go tool pprof -seconds 30 http://localhost:6060/debug/pprof/profile

```

This profiles the exact functions in [`stun/client.go`](https://github.com/bannedbook/fanqiang/blob/main/stun/client.go) and [`dns_box.go`](https://github.com/bannedbook/fanqiang/blob/main/dns_box.go) that handle packet processing, revealing CPU hotspots and goroutine counts. You can also use `expvar` hooks to export custom counters for DNS lookup latency and STUN round-trip times.

## Capturing Android System Metrics

For the Android UI layer located in `app/src/main`, use Android Studio Profiler to monitor frame rendering times, memory usage, and battery impact. The DNS proxy uses UDP/TCP under the hood, which appears in the Profiler's Network pane. Ensure [`AndroidManifest.xml`](https://github.com/bannedbook/fanqiang/blob/main/AndroidManifest.xml) contains the necessary network permissions for profiling that uses network sockets.

Capture runtime logs from the ChannelMonitor thread using adb:

```bash

# Run once per test session

adb logcat -v time -s ChannelMonitor > /tmp/channel_monitor.log &

# … after exercising the app …

adb logcat -c               # clear the buffer for next run

```

## Analyzing Latency from ChannelMonitor Logs

Process the captured log files to calculate DNS operation latency. The logs contain `REGISTER` events (when operations start) and `WAIT` events (when channels become ready).

Extract latency metrics using this awk script:

```bash
awk '
/REGISTER/ { start[$4] = $1 }
/WAIT/ && $4 in start { printf "%s %dms\n", $4, $1 - start[$4] }
' /tmp/channel_monitor.log | sort -k2 -n

```

This calculates the time delta between registration and readiness for each channel, sorting results to identify slow operations.

## Aggregating Metrics for Unified Monitoring

Combine these data sources into a centralized monitoring stack:

- **Logcat ingestion**: Pipe Android logs to Fluent Bit → Elasticsearch for searchable, time-series log analysis
- **Go metrics**: Export pprof data via a Prometheus exporter such as Pyroscope for continuous profiling
- **Custom Kotlin counters**: Expose DNS latency histograms via a lightweight HTTP `/metrics` endpoint for Prometheus scraping

This unified approach provides a dashboard showing DNS request latency distributions, Go routine CPU usage, and Android UI frame-time histograms.

## Summary

- **Instrument ChannelMonitor**: Modify [`fqnews/core/src/main/java/com/github/shadowsocks/net/ChannelMonitor.kt`](https://github.com/bannedbook/fanqiang/blob/main/fqnews/core/src/main/java/com/github/shadowsocks/net/ChannelMonitor.kt) to emit timestamps in `register()` and wait handlers to capture DNS proxy latency
- **Enable Go pprof**: Add `net/http/pprof` imports to [`fqnews2/libcore/http.go`](https://github.com/bannedbook/fanqiang/blob/main/fqnews2/libcore/http.go) to expose CPU and memory profiles for the networking core
- **Capture Android logs**: Use `adb logcat -s ChannelMonitor` to collect runtime data and process with `awk` to calculate per-operation latency
- **Profile system-wide**: Use Android Studio Profiler for UI thread analysis and resource monitoring on the Android frontend

## Frequently Asked Questions

### How do I measure DNS request latency in bannedbook/fanqiang?

Modify the `register()` method in [`ChannelMonitor.kt`](https://github.com/bannedbook/fanqiang/blob/main/ChannelMonitor.kt) to log `System.currentTimeMillis()` when channels register and when they become ready. The difference between these timestamps represents the selector latency for each DNS operation, which you can extract using the provided `awk` script.

### Where should I add pprof endpoints in the Go libcore?

Add the pprof HTTP server initialization to [`fqnews2/libcore/http.go`](https://github.com/bannedbook/fanqiang/blob/main/fqnews2/libcore/http.go) within an `init()` function. This exposes `/debug/pprof/` endpoints for the entire `libcore` package, including the STUN client routines in [`stun/client.go`](https://github.com/bannedbook/fanqiang/blob/main/stun/client.go) and DNS handlers in [`dns_box.go`](https://github.com/bannedbook/fanqiang/blob/main/dns_box.go).

### Can I monitor fanqiang performance without modifying the source code?

You can capture basic metrics using Android Studio Profiler and standard `adb logcat` without code changes, but precise DNS latency measurement requires adding timestamp instrumentation to [`ChannelMonitor.kt`](https://github.com/bannedbook/fanqiang/blob/main/ChannelMonitor.kt). The Go pprof endpoints also require the minimal code addition shown above to expose runtime metrics.

### What tools analyze the ChannelMonitor logs most effectively?

Use `awk` for quick command-line analysis of latency patterns, or import the log data into Grafana or Elasticsearch for visualization. The `printLog` function calls in the Kotlin source can be redirected to structured logging systems like ELK for historical trend analysis.