This is the multi-page printable view of this section. Click here to print.
Best Practice
- 1: Storage Service
- 2: Data Source
- 3: Events Watch
- 4: Profiling
- 5: Network Drop Monitoring
- 6: TCP Retransmission Tracing
- 7: IRQ Tracing
1 - Storage Service
📖 Overview
HUATUO supports persisting Linux kernel events collected by the Tracer and AutoTracing data to external storage backends. Both Elasticsearch and OpenSearch are supported.
After serialization to JSON, collected events are written concurrently to the local node directory (huatuo-local/) and the configured remote storage backend. The local directory retains a local copy of events; the remote backend provides durable storage and structured query capabilities.
This document covers configuration and verification for both Elasticsearch and OpenSearch. Examples use Docker deployments. In production, replace the addresses with your actual service endpoints — the configuration format is the same.
🎯 Use Cases
Kubernetes Cloud-Native Fault Tracing
In containerized environments, kernel events such as Pod OOM and node Hung Task are transient — logs are often purged shortly after the event occurs. By writing events to Elasticsearch or OpenSearch, operations teams can query the historical timeline of anomalies by time range and precisely identify the root cause of intermittent failures during post-incident reviews.
AI Compute Cluster Stability Auditing
During long-running GPU training workloads, the historical distribution of events such as ras hardware errors and iotracing I/O latency is critical for capacity planning and hardware health assessment. Persisting collected data enables aggregate queries to establish node stability baselines and supports proactive maintenance decisions.
Compliance and Event Retention
Security compliance standards require that system anomaly events be traceable. Writing HUATUO-captured kernel events to OpenSearch and configuring an index lifecycle policy satisfies compliance requirements for event retention periods and query capabilities.
Observability Platform Integration
Both Elasticsearch and OpenSearch provide native data source integrations with Grafana. Once HUATUO events are written to storage, you can build kernel event trend dashboards in Grafana, overlaid with application-layer metrics for historical analysis and alert review.
💎 Value
| Dimension | Local Storage Only | With External Storage Backend |
|---|---|---|
| Data Durability | Limited by node disk capacity; may be lost on restart | Persisted to distributed storage; supports long-term retention |
| Query Capability | No structured queries; relies on file search | Full-text search, field filtering, time-range aggregation |
| Visualization | Not supported | Direct integration with Grafana, Kibana, and similar platforms |
| Multi-node Aggregation | Data scattered across individual nodes | Centralized storage; supports cross-node queries |
| Compliance Retention | Difficult to meet retention requirements | Configurable index lifecycle policies; meets compliance retention requirements |
🚀 Usage
OpenSearch V2
1. Deploy OpenSearch
docker pull opensearchproject/opensearch:2.6.0
docker run -d --name opensearch --network host \
-e "discovery.type=single-node" \
opensearchproject/opensearch:2.6.0
2. Verify Service Status
curl -k -u admin:admin https://localhost:9200
Example response:
{
"name" : "22ca72df78c0",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "yxb3foceQVKzXXO6bHpPHQ",
"version" : {
"distribution" : "opensearch",
"number" : "2.6.0",
"build_type" : "tar",
"build_hash" : "7203a5af21a8a009aece1474446b437a3c674db6",
"build_date" : "2023-02-24T18:57:04.388618985Z",
"build_snapshot" : false,
"lucene_version" : "9.5.0",
"minimum_wire_compatibility_version" : "7.10.0",
"minimum_index_compatibility_version" : "7.0.0"
},
"tagline" : "The OpenSearch Project: https://opensearch.org/"
}
If verification fails, check the container logs:
docker logs opensearch
3. Configure huatuo-bamai
Add the following configuration to huatuo-bamai.conf. The default username and password for the OpenSearch container image are both admin. For a full description of storage configuration options, see the Configuration Guide.
[Storage.Elasticsearch]
Address = "https://127.0.0.1:9200"
Index = "huatuo_bamai"
Username = "admin"
Password = "admin"
4. Start huatuo-bamai
Use --config-dir to specify the directory containing the configuration file:
./_output/bin/huatuo-bamai --region dev --config-dir .
When files (e.g., net_rx_latency) appear in the local storage directory huatuo-local/, kernel events have been successfully captured. Query data from OpenSearch with:
curl -k -u admin:admin \
-X GET "https://localhost:9200/huatuo_bamai/_search?pretty" \
-H "Content-Type: application/json" \
-d '{"query": {"match_all": {}}}'
Example response:
{
"_index" : "huatuo_bamai",
"_id" : "yjPG_50Bu_OF-hukxKR7",
"_score" : 1.0,
"_source" : {
"hostname" : "hostname",
"region" : "dev",
"uploaded_timestamp" : "2026-05-07T00:11:49.753166222Z",
"tracer_name" : "net_rx_latency",
"observed_timestamp" : "2026-05-07T00:11:49.753166222Z",
"tracer_type" : "event",
"tracer_data" : {
"comm" : "<nil>",
"pid" : 0,
"latency_stage" : "RX_STAGE_NETIF",
"latency_ms" : 1776078133565,
"tcp_saddr" : "127.0.0.1",
"tcp_daddr" : "127.0.0.1",
"tcp_sport" : 37736,
"tcp_dport" : 9200,
"tcp_seq" : 1080592402,
"tcp_ack_seq" : 2465063876,
"packet_len_bytes" : 781
}
}
}
To get the total document count without listing individual records:
curl -k -u admin:admin -X GET "https://localhost:9200/huatuo_bamai/_count?pretty"
Example response: the count value equals the total number of written records.
{
"count" : 2680,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
}
}
Elasticsearch V8
1. Deploy Elasticsearch
docker pull docker.elastic.co/elasticsearch/elasticsearch:8.15.5
docker run -d --name elasticsearch --network host \
-e "discovery.type=single-node" \
-e "ES_JAVA_OPTS=-Xms1g -Xmx1g" \
-e "ELASTIC_PASSWORD=123456" \
docker.elastic.co/elasticsearch/elasticsearch:8.15.5
2. Verify Service Status
curl -k -u elastic:123456 https://localhost:9200
Example response:
{
"name" : "ab0b562f8dbd",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "aVfOVgJTQXuhZ3HGotK3ww",
"version" : {
"number" : "8.15.5",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "b10896bcfe167cce44a84ba2771d101fb596d40d",
"build_date" : "2024-11-21T22:06:13.985834967Z",
"build_snapshot" : false,
"lucene_version" : "9.11.1",
"minimum_wire_compatibility_version" : "7.17.0",
"minimum_index_compatibility_version" : "7.0.0"
},
"tagline" : "You Know, for Search"
}
3. Configure huatuo-bamai
Add the following configuration to huatuo-bamai.conf. The default username for the Elasticsearch container image is elastic; the password is set via the ELASTIC_PASSWORD environment variable. For a full description of storage configuration options, see the Configuration Guide.
[Storage.Elasticsearch]
Address = "https://127.0.0.1:9200"
Index = "huatuo_bamai"
Username = "elastic"
Password = "123456"
4. Start huatuo-bamai
Use --config-dir to specify the directory containing the configuration file:
./_output/bin/huatuo-bamai --region dev --config-dir .
When files (e.g., net_rx_latency) appear in the local storage directory huatuo-local/, kernel events have been successfully captured. Query data from Elasticsearch with:
curl -k -u elastic:123456 \
-X GET "https://localhost:9200/huatuo_bamai/_search?pretty" \
-H "Content-Type: application/json" \
-d '{"query": {"match_all": {}}}'
Example response:
{
"_index" : "huatuo_bamai",
"_id" : "WtNZAJ4BQ8x-thPHEY1i",
"_score" : 1.0,
"_source" : {
"hostname" : "hostname",
"region" : "dev",
"uploaded_timestamp" : "2026-05-07T02:51:37.696263325Z",
"tracer_name" : "net_rx_latency",
"observed_timestamp" : "2026-05-07T02:51:37.696263325Z",
"tracer_type" : "event",
"tracer_data" : {
"comm" : "<nil>",
"pid" : 0,
"latency_stage" : "RX_STAGE_NETIF",
"latency_ms" : 1776078133565,
"tcp_saddr" : "127.0.0.1",
"tcp_daddr" : "127.0.0.1",
"tcp_sport" : 2379,
"tcp_dport" : 36706,
"tcp_seq" : 950542706,
"tcp_ack_seq" : 1960972383,
"packet_len_bytes" : 91
}
}
}
To get the total document count without listing individual records:
curl -k -u elastic:123456 -X GET "https://localhost:9200/huatuo_bamai/_count?pretty"
Example response: the count value equals the total number of written records.
{
"count" : 2680,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
}
}
Elasticsearch V7
Elasticsearch V7 uses HTTP by default. Replace https with http in all commands.
1. Deploy Elasticsearch
docker pull docker.elastic.co/elasticsearch/elasticsearch:7.10.1
docker run -d --name elasticsearch --network host \
-e "discovery.type=single-node" \
-e "ES_JAVA_OPTS=-Xms1g -Xmx1g" \
-e "ELASTIC_PASSWORD=123456" \
docker.elastic.co/elasticsearch/elasticsearch:7.10.1
2. Verify Service Status
curl -k -u elastic:123456 http://localhost:9200
Example response:
{
"name" : "d88c9e8df48b",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "_ZZefWx4SniAc255t_lIVg",
"version" : {
"number" : "7.10.1",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "1c34507e66d7db1211f66f3513706fdf548736aa",
"build_date" : "2020-12-05T01:00:33.671820Z",
"build_snapshot" : false,
"lucene_version" : "8.7.0",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
3. Configure huatuo-bamai
[Storage.Elasticsearch]
Address = "http://127.0.0.1:9200"
Index = "huatuo_bamai"
Username = "elastic"
Password = "123456"
4. Start huatuo-bamai
Use --config-dir to specify the directory containing the configuration file:
./_output/bin/huatuo-bamai --region dev --config-dir .
When files (e.g., net_rx_latency) appear in the local storage directory huatuo-local/, kernel events have been successfully captured. Query data from Elasticsearch with:
curl -k -u elastic:123456 \
-X GET "http://localhost:9200/huatuo_bamai/_search?pretty" \
-H "Content-Type: application/json" \
-d '{"query": {"match_all": {}}}'
To get the total document count:
curl -k -u elastic:123456 -X GET "http://localhost:9200/huatuo_bamai/_count?pretty"
⚙️ How It Works
System Architecture
The HUATUO Storage module runs on each node. It writes kernel events captured by the Tracer to the local directory and to Elasticsearch or OpenSearch. Both backends share the same [Storage.Elasticsearch] configuration interface and are differentiated by address.
The remote write path uses the ES/OpenSearch Bulk API (_bulk): events are queued in an in-memory buffer and submitted in batches by background workers based on size and time thresholds, with transport-layer retries on transient failures.
graph TB
subgraph kernel["Linux Kernel"]
K1[Kernel Events]
K2[AutoTracing]
end
subgraph huatuo["HUATUO Agent (node-level)"]
T["Tracer Layer"]
L["Local Directory\nhuatuo-local/"]
S["Storage Module\nBulkIndexer Buffer"]
end
subgraph backends["Storage Backends"]
ES[Elasticsearch]
OS[OpenSearch]
end
kernel --> T
T --> L
T --> S
S -->|Bulk API + auto retry| ES
S -->|Bulk API + auto retry| OS
Filter semantics
The internal storage query API combines filters with AND. SQLite and Elasticsearch share the following contract for non-null scalar values:
| Operator | Meaning |
|---|---|
OpEq, OpNe |
Equal / not equal; missing and null values do not match |
OpGt, OpGte, OpLt, OpLte |
Strict / inclusive range bounds |
OpIn |
Membership in a non-empty slice or array of scalar values |
OpExists, OpNotExists |
Has / lacks a non-null queryable value; omit Value |
Empty strings are literal values: OpEq("") matches an empty string, while
OpNe("") requires an existing, non-null, non-empty string. To query missing
fields, use OpNotExists; OpEq("") no longer means field absence. Host profiling
queries use OpNotExists on container_hostname to preserve their selection.
Operands accept built-in string, boolean, integer, and finite floating-point
types. Range comparisons exclude booleans. time.Time and timeutil.Timestamp
are normalized to UTC strings with nine fractional digits, including in OpIn.
Nil, pointers, objects, nested collections, and non-finite numbers return
driver.ErrInvalidQuery. Integer range bounds retain their original JSON
precision; each backend’s supported numeric domain and field mapping still apply.
For Elasticsearch, callers must supply the exact indexed field: use the original
field for a keyword mapping, or an explicitly configured keyword subfield such
as hostname.keyword for a text mapping. The backend neither guesses subfields
nor combines text and keyword queries. OpNe applies exists and must_not term
to the same field. Full-text matching and array equality are outside this scalar
comparison contract.
Elasticsearch existence refers to indexed values, not _source key presence.
Mapping options such as ignore_above, null_value, and normalizers can change
existence or equality behavior. Use mappings that preserve the intended scalar
values when requiring equivalence with SQLite. A date field cannot be compared
to an empty string; use an existence operator when the intent is a populated date.
Write Flow
Save returns immediately after the event is buffered. Background workers flush the buffer to the remote backend when any of the following triggers fire: byte threshold, time threshold, or process shutdown. The local directory write is synchronous and independent of the remote Bulk path.
sequenceDiagram
participant T as Tracer Layer
participant L as Local Directory (huatuo-local/)
participant S as Storage Module (BulkIndexer)
participant B as ES / OpenSearch
T->>S: Kernel event captured, serialized to JSON
par Local path (sync)
S->>L: Write to local file
and Remote path (async batch)
S->>S: Enqueue into bulk buffer, return immediately
Note over S: Flush on 5 MB / 1 s / shutdown
S->>B: POST /_bulk (multiple records)
B-->>S: 200 OK + per-item results
Note over S: Failed items reported via OnFailure callback
end
Bulk Write Mechanism
Buffering and Flush Triggers
| Parameter | Value | Meaning |
|---|---|---|
FlushBytes |
5 MB | Flush when accumulated bytes reach the threshold |
FlushInterval |
1 s | Force-flush 1 second after the previous flush |
NumWorkers |
4 | Concurrent workers submitting Bulk requests |
| Process shutdown | Close(ctx) |
SIGTERM/SIGINT triggers a 10 s bounded drain |
Two-Tier Retry Policy
Bulk failures are split into two layers with different retry semantics:
| Layer | Trigger | Behavior | Retried? |
|---|---|---|---|
| Whole-batch retry | Transport error (connect / timeout / TLS) HTTP status: 429 / 502 / 503 / 504 |
Client retries with exponential backoff: 100 ms → 200 ms → 400 ms → 800 ms, up to 3 attempts | ✅ auto |
| Whole-batch reject | HTTP status: 400 / 401 / 403 / 404 / 413, etc. |
Not retried; all records in the batch are dropped, an error is logged via OnError |
❌ drop |
| Per-item failure | 200 OK with per-item error: version conflict, mapping error, document too large | Not retried; only the failed item is dropped, OnFailure logs index/id/status/type/reason |
❌ drop |
| Per-item success | 200 OK with per-item success | Primary write succeeded; replica failures produce a warning with index, document ID, and failed shard count | — |
Why this design: 429/5xx and transport errors signal transient remote unavailability where retries are effective; 4xx (except 429) and per-item errors are client-side semantic issues (data shape, permissions) where retries would only amplify the failure — they should be surfaced via logs for human investigation.
Batched Reads
Query(ctx, query, consume) calls the consumer synchronously for each nonempty
batch. Limit caps the total records delivered after Offset matching records
are skipped. storage.Store.Query requires a positive Limit and non-negative
Offset and BatchSize before calling a backend; backends consume validated input.
BatchSize controls each batch, defaults to 100 when zero, and is capped by the
remaining total. Callers must supply a positive total limit when they need data.
Count and Values use only Filters from the query and ignore Sorts, Limit,
Offset, and BatchSize, including zero and negative pagination values. Count
counts all matching records. Values uses its separate size argument, whose
non-negative constraint is checked by storage.Store.Values before dispatch;
backends do not repeat pagination or size validation for these operations.
Local file storage remains write-only.
SQLite streams one SELECT ... LIMIT ... OFFSET ... through rows. A consumer
must not query the same SQLite backend synchronously while rows hold its single
connection. Job recovery reads 1000 Jobs at a time using ascending IDs, then
starts their supervisors after the read closes; status changes cannot shift the
next page. Elasticsearch 7/8 and OpenSearch use Scroll snapshots, including for
offsets beyond the result window. Deep offsets still require reading and
skipping earlier records. Separate API calls do not share a snapshot.
Each batch has independent ownership. Callback errors, decoding errors, backend errors, and cancellation stop the query immediately; earlier callback effects are not rolled back. Scroll cleanup uses a separate bounded context even after cancellation. Query requests are not retried because a lost Scroll response may already have advanced the cursor. Write retry behavior is unchanged.
API defaults apply only when limit is omitted: Job lists default to 100 with a
maximum of 1000; raw Profile pages default to 20 with a maximum of 100 and retain
the 64 MiB response budget. Explicit zero, negative, or oversized limits are rejected. One additional
record is fetched to determine has_more.
Profile flamegraphs merge batches as they arrive, with a total budget of 100000 windows. One extra record detects overflow and rejects the query with an instruction to narrow its time range or filters. Read or merge failure never returns a partial flamegraph. Input batches can be released as processing advances; the merged profile still grows with distinct stacks.
Response Errors
HTTP success does not guarantee a complete result. Query and Values return
an error when a search times out or a shard fails. Query stops before
delivering the failing page; already consumed batches remain consumed. Values
does not return partial results.
Count returns zero and an error on shard failure. A search timeout takes
precedence over shard failures. Shard errors include the failed shard count and
the first failure’s available type, reason, and nested causes.
After an HTTP success response, DeleteByQuery checks for timeouts and failures
in that order and returns the first error with the reported deleted count. HTTP errors
return zero and an error, except that a missing index returns zero without an error.
Any completed deletions are not rolled back.
Direct writes and deletes retain primary-operation success when replicas fail;
they log a warning with the available failure details. Bulk writes also report
replica failures through warnings, without retrying the successful primary write.
HTTP errors include the response status and available Elasticsearch error details. Unrecognized or oversized error bodies are reported as text, limited to 4 KiB plus a truncation marker.
Data-Loss Scenarios
In all three scenarios below, Save returns nil but the event never reaches the index:
- Abnormal process exit:
SIGKILLor host power loss drops whatever is still buffered in the BulkIndexer (the local directory still keeps a copy).- Mitigation: SIGTERM/SIGINT trigger graceful shutdown;
Closeforce-flushes the buffer with a 10 s deadline.
- Mitigation: SIGTERM/SIGINT trigger graceful shutdown;
- Whole-batch permanent rejection: 4xx (non-429) errors discard every record in the batch. Common causes: disabled index, expired credentials, document exceeding the cluster’s
http.max_content_length.- Diagnosis:
OnErrorlog includes ES’stypeandreason.
- Diagnosis:
- Permanent per-item failure: mapping conflict, version conflict, malformed document.
- Diagnosis:
OnFailurelog identifies the record byindex/id.
- Diagnosis:
The local directory is always a fallback: even if remote writes are lost, events remain available in
huatuo-local/as the eventual-consistency safety net.
Problems This Solves
Replacing per-event Index API calls with a buffered BulkIndexer + auto-retry addresses four classes of problems:
| Problem | Old approach bottleneck | Bulk approach improvement |
|---|---|---|
| TLS handshake CPU cost | One HTTPS handshake per event saturated CPU under FIPS/RSA-PSS | Many events share one connection and one handshake; TLS PSK tickets cached |
| Remote RTT throughput ceiling | One round-trip per event capped node-level write rate | One Bulk request carries up to 5 MB; throughput scales with batch size |
| Transient remote jitter / 429 throttle | A single failure dropped the event with no retry | Client-level retry absorbs short-lived faults |
| Decoupling tracer layer from backend | Slow remote backed pressure into capture, delaying tracing | Async buffer decouples capture from network — capture is no longer blocked on remote latency |
🌟 Stay Connected
Kernel observation time
Event documents can include a top-level kernel_observed_timestamp containing
a UTC date-time. observed_timestamp is userspace observation time, and
uploaded_timestamp is storage write time. These fields do not fill one
another. Older documents and events without kernel timestamps omit the new
field; historical data is not migrated automatically. The raw
kernel_observed_ns stays in process for correlation and is not
written to new documents.
2 - Data Source
HUATUO integrates with Prometheus for metrics collection and Elasticsearch for log storage. This document covers data source configuration and dashboard provisioning in Grafana.
Two deployment paths are supported:
- Docker Compose — recommended for development and testing; all components are pre-configured.
- Kubernetes — for production clusters; requires manual data source configuration.
Quick Start (Docker Compose)
The build/docker/ directory contains a complete stack. All default credentials and ports listed below match this setup.
cd build/docker
docker compose up -d
This starts four services on the host network:
| Service | Port | Purpose |
|---|---|---|
| Elasticsearch | 9200 | Log storage |
| Prometheus | 9090 | Metrics collection |
| Grafana | 3000 | Visualization |
| huatuo-bamai | 19704 | Agent (metrics + tracing) |
Default credentials:
| Service | Username | Password |
|---|---|---|
| Elasticsearch | elastic |
huatuo-bamai |
| Grafana | admin |
admin |
Data sources and dashboards are auto-provisioned. Access Grafana at http://<host>:3000.
Verify the Stack
# Elasticsearch
curl -s -u elastic:huatuo-bamai http://localhost:9200/_cluster/health?pretty
# Prometheus — should show huatuo target as "up"
curl -s http://localhost:9090/api/v1/targets | jq '.data.activeTargets[] | {job: .labels.job, health: .health}'
# Grafana
curl -s http://localhost:3000/api/health | jq .version
# HuaTuo metrics
curl -s http://localhost:19704/metrics | head -5
Provisioned Data Sources
The following data sources are created automatically via build/docker/grafana/datasources/:
| Name | Type | UID |
|---|---|---|
| huatuo-bamai-prom | Prometheus | huatuo-bamai-prom |
| huatuo-bamai-es | Elasticsearch | huatuo-bamai-es |
| huatuo-bamai-infinity | Infinity | huatuo-bamai-infinity-auto-flamegraph |
Provisioned Dashboards
Six dashboards are loaded from build/docker/grafana/dashboards/:
- Metric Dashboard — Host View
- Metric Dashboard — Container View
- HuaTuo Root Cause Analysis AutoTracing
- Continuous Profiling (Host)
- Continuous Profiling (Container)
- AutoTracing Flame Redirect
No manual import is needed when using Docker Compose.
Metrics Collection (Kubernetes)
1. Verify Metrics Endpoint
After deploying huatuo-bamai to Kubernetes, expose the metrics endpoint:
kubectl port-forward -n default --address=0.0.0.0 pod/huatuo-XXXX 19704:19704
Verify:
curl http://localhost:19704/metrics
Metrics output confirms the agent is running correctly.
2. Configure Prometheus Scraping
Option A: Pod Annotations
Add annotations to the Pod template metadata. This requires a Prometheus setup with Kubernetes pod service discovery enabled (e.g., kubernetes_sd_configs with role: pod).
template:
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "19704"
prometheus.io/path: "/metrics"
Option B: ServiceMonitor
Requires Prometheus Operator. Create two resources:
huatuo-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: huatuo
labels:
app: huatuo
spec:
clusterIP: None
ports:
- name: metrics
port: 19704
targetPort: 19704
protocol: TCP
selector:
app: huatuo
huatuo-servicemonitor.yaml:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: huatuo
namespace: default
labels:
release: prometheus
spec:
namespaceSelector:
matchNames:
- default
selector:
matchLabels:
app: huatuo
endpoints:
- port: metrics
path: /metrics
interval: 30s
scrapeTimeout: 10s
3. Query Metrics in Prometheus
huatuo_*
If results are returned, metrics collection is working properly.
Log Collection (Kubernetes)
Query logs from Elasticsearch:
curl -u elastic:<password> "http://<es-host>:9200/huatuo_bamai/_search?pretty"
Replace <password> and <es-host> with your Elasticsearch credentials and address.
Manual Grafana Data Source Configuration
When not using Docker Compose (e.g., external Grafana), configure data sources manually.
Prometheus Data Source
Refer to build/docker/grafana/datasources/prometheus.yaml for the provisioning file, or configure via Grafana UI:
- URL:
http://<prometheus-host>:9090 - Access: Server (proxy)
Elasticsearch Data Source
Configure via Grafana UI or provisioning:
- URL:
http://<es-host>:9200 - Authentication: Basic Authentication
- Username:
elastic - Password:
<your-elasticsearch-password> - Index name:
huatuo_bamai - Time field name:
uploaded_timestamp
Dashboard Import
When using Docker Compose, dashboards are provisioned automatically. To import additional dashboards from the HUAUO console:
- Access
http://console.huatuo.tech/dashboards(Username:huatuo, Password:huatuo1024) - Select the desired dashboard
- Click Export -> Export as JSON
- Check “Export the dashboard to use in another instance”
- Click Copy to clipboard
Then in your Grafana instance:
- Navigate to Dashboards -> Import
- Paste the JSON content
- Click Load
- Select the correct data sources and click Import
Troubleshooting
“datasource not found” when importing dashboard
This occurs when the dashboard JSON references a datasource UID that does not exist in your Grafana instance.
Solution:
- Find your Elasticsearch datasource UID from the Grafana UI URL:
http://<grafana-host>:3000/connections/datasources/edit/<uid> - In the dashboard JSON, replace all occurrences of
"uid": "${DS_HUATUO-BAMAI-ES}"with your actual UID - Re-import the dashboard
Prometheus target shows “down”
- Verify huatuo-bamai is running:
curl http://<host>:19704/metrics - Check Prometheus configuration matches the agent’s address and port
- For Kubernetes: ensure pod annotations are correct or ServiceMonitor selector matches
Elasticsearch index is empty
- Verify Elasticsearch is reachable:
curl -u elastic:<password> http://<host>:9200/_cat/indices - Check huatuo-bamai config
[Storage.ES]section has correctAddress,Username,Password - Default index name is
huatuo_bamai
“socket path already exists” on startup
This occurs when a previous huatuo-bamai process was not cleanly stopped.
Solution:
rm -f /var/run/huatuo-toolstream.sock
3 - Events Watch
📖 Overview
/v1/events/watch is HUATUO’s real-time kernel event subscription endpoint. A single HTTP POST long-lived connection streams kernel anomaly events from the node continuously. Events are wrapped in the CloudEvents 1.0 specification and delivered via the Server-Sent Events (SSE) protocol.
🎯 Use Cases
Kernel event subscription surfaces OS-level anomaly signals directly to higher-level systems, eliminating the latency and overhead of traditional polling. The following are typical integration scenarios.
Fault Self-Healing
Kernel events are the primary signal source for self-healing decisions. After subscribing to events/watch, a healing controller can trigger remediation the moment an event occurs, without waiting for an alert to propagate through a monitoring pipeline:
- OOM self-healing: On receiving a
memory_oom_killevent, immediately scale, restart, or drain traffic from the triggering container. Reduces service interruption from minutes to seconds. - Hung task self-healing: On receiving a
hungtaskevent, automatically cordon the node and evict Pods to prevent cascading blockage from spreading across the cluster. - Network fault self-healing: On receiving a
netdev_txqueue_timeoutornetdev_bonding_lacpevent, trigger a NIC reset or traffic failover to restore the network link within minutes. - I/O storm self-healing: On receiving an
iotracingevent, dynamically throttle the affected container’s disk I/O quota via cgroup blkio to protect co-located services on the same node.
Observability Platforms
Integrating HUATUO kernel events into an observability platform adds a kernel-level perspective beyond application metrics and logs:
- Event timeline correlation: Overlay
softlockup,memory_oom_kill, and other kernel events onto Grafana timelines, aligning them precisely with application error rates and latency curves for root-cause analysis. - Anomaly-driven alerting: Replace fixed-threshold alerts with kernel events to reduce false positives. For example, a
rashardware error event triggers a high-priority alert directly, without relying on a CPU error rate crossing a threshold. - Capacity and stability analysis: Subscribe to
memburst,dload, and other AutoTracing events over time to establish a node stability baseline and provide kernel-level data for capacity planning. - Multi-dimensional drill-down: Events carry container ID, namespace, region, and other context fields. Alert links can drill down directly to the corresponding Pod, Node, or Region view.
Security Auditing and Compliance
- Anomalous behavior detection: A cluster of
memory_oom_kill,hungtask, orsoftlockupevents outside business peak hours may indicate resource abuse or a malicious workload, triggering a security review workflow. - Event retention and traceability: Write the CloudEvents stream to a message queue (Kafka, Pulsar) or object storage to satisfy the event retention requirements of security compliance frameworks.
Chaos Engineering and Load Testing
- Fault injection verification: After injecting network latency or memory pressure via a chaos engineering platform, subscribe to
net_rx_latencyandmemburstevents in real time to verify the fault is active, replacing manual observation. - Load test baseline: Subscribe to all events during a load test. The timestamp of the first kernel anomaly event precisely marks the system’s stress threshold.
AIOps
- Event-driven root-cause analysis: Feed kernel events as features into AI/ML models alongside application metrics for multi-dimensional root-cause inference, reducing manual investigation time.
- Predictive maintenance: Model
rashardware errors andnetdev_bonding_lacphardware-layer events to detect anomalies before a device fails completely, triggering proactive migration. - Intelligent suppression and aggregation: Automatically aggregate similar events within the same time window to avoid alert storms. Deliver a concise root-cause summary to on-call engineers.
💎 Value
| Dimension | Traditional Approach | With HUATUO events/watch |
|---|---|---|
| Timeliness | Alert trigger latency: 1–5 minutes | Real-time kernel event push; latency < 1 s |
| Signal accuracy | Metric threshold-based; high false-positive rate | Events originate from kernel decisions; false-positive rate near zero |
| Context richness | Limited metric dimensions | Full context: container, node, region, and more |
| Integration cost | Requires custom eBPF collection or a third-party agent | Single HTTP POST to subscribe; standard CloudEvents format |
| Protocol compatibility | Vendor-specific formats | Follows CloudEvents 1.0; compatible with any conformant platform |
🚀 Usage
1. CloudEvents Specification
1.1 CloudEvents 1.0 Envelope Fields
Each pushed event is a JSON object conforming to the CloudEvents 1.0 specification:
| Field | Type | Description |
|---|---|---|
specversion |
string | Fixed value "1.0" |
id |
string | Unique event identifier (UUID v4), generated independently per event |
source |
string | Event source path, format: /huatuo/{hostname}/{tracer_name} |
type |
string | Fixed value "tech.huatuo.kernel.event" |
datacontenttype |
string | Fixed value "application/json" |
time |
string | Event collection timestamp (RFC 3339, nanosecond precision, UTC) |
data |
object | Event payload — the WatchEventData struct |
1.2 HUATUO Event Payload (WatchEventData)
The data field contains the standard HUATUO event record:
{
"specversion": "1.0",
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"source": "/huatuo/node-1/memory_oom_kill",
"type": "tech.huatuo.kernel.event",
"datacontenttype": "application/json",
"time": "2026-05-18T10:23:45.123456789Z",
"data": {
"hostname": "node-1",
"region": "cn-beijing",
"observed_timestamp": "2026-05-18T10:23:45Z",
"tracer_name": "memory_oom_kill",
"tracer_id": "abc123",
"tracer_run_type": "auto",
"container_id": "d3f1a2b4c5e6",
"container_hostname": "app-pod",
"container_host_namespace": "prod",
"container_type": "docker",
"container_qos": "guaranteed"
}
}
WatchEventData field reference:
| Field | Type | Description |
|---|---|---|
hostname |
string | Node hostname |
region |
string | Region where the node is located |
observed_timestamp |
string | UTC time when the event producer observed the event in userspace |
kernel_observed_timestamp |
string | Optional UTC time when the kernel observed the event |
tracer_name |
string | Name of the tracer that triggered the event (see the event list below) |
tracer_id |
string | Unique ID of this event instance |
tracer_run_type |
string | Collection mode: auto (triggered automatically) or manual |
container_id |
string | Container ID (present for container-level events) |
container_hostname |
string | Container hostname |
container_host_namespace |
string | Namespace of the container |
container_type |
string | Container runtime type (docker, containerd, etc.) |
container_qos |
string | Container QoS class (unknown, guaranteed, burstable, or besteffort) |
2. Supported Kernel Events
tracer_name |
Description |
|---|---|
memory_oom_kill |
Out-of-memory (OOM Killer) triggered event |
hungtask |
Kernel task stuck in D state (Hung Task) detection |
softlockup |
CPU soft lockup detection |
ras |
Hardware reliability (RAS) errors, such as ECC memory errors |
dropwatch |
Kernel network packet drop (Drop Watch) events |
netdev_events |
Network device state change events (Link Up/Down, etc.) |
netdev_txqueue_timeout |
Network device transmit queue timeout events |
netdev_bonding_lacp |
Bond device LACP protocol anomaly events |
net_rx_latency |
Network receive latency anomaly events |
sched_tick |
Scheduler tick interval tracing events |
memory_reclaim_events |
Memory reclaim anomaly events |
cpuidle |
CPU idle rate anomaly (AutoTracing, auto-triggered) |
cpusys |
CPU system-mode usage anomaly (AutoTracing, auto-triggered) |
dload |
System load anomaly (AutoTracing, auto-triggered) |
iotracing |
I/O latency anomaly (AutoTracing, auto-triggered) |
memburst |
Memory usage spike anomaly (AutoTracing, auto-triggered) |
3. POST Request Reference
3.1 Endpoint
POST /v1/events/watch
3.2 Request Headers
Authorization: Bearer <node-token>
Content-Type: application/json
3.3 Request Body
{
"filters": {
"tracer_name": "<regex>",
"hostname": "<regex>",
"container_hostname": "<regex>",
"container_host_namespace": "<regex>",
"container_qos": "<regex>",
"region": "<regex>"
}
}
filters field reference:
| Field | Type | Required | Description |
|---|---|---|---|
tracer_name |
string | No | Filter by tracer name; supports regular expressions |
hostname |
string | No | Filter by node hostname; supports regular expressions |
container_hostname |
string | No | Filter by container hostname; supports regular expressions |
container_host_namespace |
string | No | Filter by container namespace; supports regular expressions |
container_qos |
string | No | Filter by container QoS; supports regular expressions |
region |
string | No | Filter by region; supports regular expressions |
- All filter fields are optional. Omitting or leaving a field empty matches all values.
- When multiple fields are specified, all conditions must be satisfied simultaneously (AND semantics).
- Filters are evaluated server-side; only matching events are pushed to the client.
3.4 Response Format (SSE Stream)
After the connection is established, the server continuously pushes events in SSE format:
data: {"specversion":"1.0","id":"...","source":"/huatuo/node-1/memory_oom_kill",...}\n\n
The server also sends periodic heartbeat comment lines to keep the connection alive:
: ping\n
4. HTTP Server Event Stream Configuration
Configure the event stream controls under [HTTPServer]:
[HTTPServer]
# Maximum number of concurrent client connections. New connections receive HTTP 429 when the limit is reached.
# Default: 100
MaxEventStreamClients = 100
# SSE heartbeat interval in seconds. Prevents proxies and load balancers from closing idle connections.
# The connection is closed after three consecutive heartbeat write failures.
# Default: 30
EventStreamKeepAliveIntervalSeconds = 30
| Field | Default | Description |
|---|---|---|
MaxEventStreamClients |
100 | Maximum concurrent /v1/events/watch connections. Excess connections receive HTTP 429. |
EventStreamKeepAliveIntervalSeconds |
30 | Heartbeat interval. Keep it below the upstream proxy’s idle timeout. |
5. curl Examples
5.1 Subscribe to All Kernel Events
curl -s -N -X POST http://<node-ip>:19704/v1/events/watch \
-H "Authorization: Bearer <node-token>" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-H "Cache-Control: no-cache" \
-H "Connection: keep-alive" \
-d '{}'
5.2 Subscribe to OOM Events Only
curl -s -N -X POST http://<node-ip>:19704/v1/events/watch \
-H "Authorization: Bearer <node-token>" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-H "Cache-Control: no-cache" \
-H "Connection: keep-alive" \
-d '{"filters": {"tracer_name": "^memory_oom_kill$"}}'
5.3 Subscribe to Network Events on a Specific Node
curl -s -N -X POST http://<node-ip>:19704/v1/events/watch \
-H "Authorization: Bearer <node-token>" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-H "Cache-Control: no-cache" \
-H "Connection: keep-alive" \
-d '{
"filters": {
"hostname": "^node-1$",
"tracer_name": "netdev|dropwatch|net_rx_latency"
}
}'
5.4 Subscribe to Container Events in the prod Namespace
curl -s -N -X POST http://<node-ip>:19704/v1/events/watch \
-H "Authorization: Bearer <node-token>" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-H "Cache-Control: no-cache" \
-H "Connection: keep-alive" \
-d '{
"filters": {
"container_host_namespace": "^prod$"
}
}'
Note: The
-Nflag disables curl buffering, causing SSE events to be printed to the terminal immediately.
6. Generated Go Client Example
POST /v1/events/watch is part of the Node OpenAPI contract. The generated
client supplies the request and event types used below.
package main
import (
"bufio"
"context"
"encoding/json"
"fmt"
"log/slog"
"net/http"
"os"
"strings"
"time"
nodeapi "github.com/ccfos/huatuo/apis/v1/node"
)
func watchEvents(
ctx context.Context,
baseURL string,
token string,
filters nodeapi.WatchEventFilters,
) error {
client, err := nodeapi.NewClient(
baseURL,
nodeapi.WithHTTPClient(&http.Client{}),
nodeapi.WithRequestEditorFn(func(_ context.Context, request *http.Request) error {
request.Header.Set("Authorization", "Bearer "+token)
request.Header.Set("Accept", "text/event-stream")
return nil
}),
)
if err != nil {
return fmt.Errorf("create Node API client: %w", err)
}
resp, err := client.WatchEvents(ctx, nodeapi.WatchEventsJSONRequestBody{
Filters: &filters,
})
if err != nil {
return fmt.Errorf("connect: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected status: %d", resp.StatusCode)
}
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
line := scanner.Text()
// skip heartbeat comment lines and blank lines
if line == "" || strings.HasPrefix(line, ":") {
continue
}
// SSE data line format: `data: <json>`
data, ok := strings.CutPrefix(line, "data: ")
if !ok {
continue
}
var event nodeapi.WatchEvent
if err := json.Unmarshal([]byte(data), &event); err != nil {
slog.Warn("parse event", "err", err)
continue
}
fmt.Printf(
"[%s] source=%s id=%s\n",
event.Time.Format(time.RFC3339Nano),
event.Source,
event.ID.String(),
)
fmt.Printf(" data: %+v\n", event.Data)
}
return scanner.Err()
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
tracerName := "memory_oom_kill|hungtask|softlockup"
err := watchEvents(ctx, "http://192.168.1.10:19704", "node-token", nodeapi.WatchEventFilters{
TracerName: &tracerName,
})
if err != nil {
slog.Error("watch events", "err", err)
os.Exit(1)
}
}
6.1 Streaming Client Selection
Use the generated Client.WatchEvents method, which returns the raw
http.Response. Do not use ClientWithResponses.WatchEventsWithResponse for
this endpoint: that helper reads the body to EOF, while an SSE stream normally
remains open until its context is canceled.
6.2 Reconnection
In production, network interruptions or service restarts will drop the connection. Use exponential backoff to reconnect:
func watchWithRetry(
ctx context.Context,
baseURL string,
token string,
filters nodeapi.WatchEventFilters,
) {
backoff := time.Second
for {
if err := watchEvents(ctx, baseURL, token, filters); err != nil {
if ctx.Err() != nil {
return
}
slog.Warn("disconnected, retrying", "err", err, "backoff", backoff)
// time.NewTimer + Stop releases the timer immediately when the context is cancelled
timer := time.NewTimer(backoff)
select {
case <-ctx.Done():
timer.Stop()
return
case <-timer.C:
}
if backoff < 30*time.Second {
backoff *= 2
}
}
}
}
⚙️ How It Works
Architecture
HUATUO Agent runs on each node. It hooks into critical kernel paths via eBPF, Kprobe, and Tracepoint, collects kernel anomaly events, applies filters, wraps them as CloudEvents, and pushes them to multiple concurrent SSE subscribers.
graph TB
subgraph kernel["Linux Kernel"]
K1[OOM Killer]
K2[Hung Task Detection]
K3[Soft Lockup Detection]
K4[RAS Hardware Errors]
K5[Network Subsystem]
K6[AutoTracing]
end
subgraph huatuo["HUATUO Agent (per node)"]
T["Tracer Collection Layer\neBPF / Kprobe / Tracepoint"]
F["Filter\nhostname / tracer / namespace / region"]
CE["CloudEvents 1.0 Wrapper\nid / source / time / data"]
EW["EventsWatch Dispatcher\nSSE connection management"]
end
subgraph clients["Subscribers"]
C1[Fault Self-Healing System]
C2[Observability Platform]
C3[AIOps System]
C4[Security Audit System]
end
kernel --> T
T --> F
F --> CE
CE --> EW
EW -->|SSE push| C1
EW -->|SSE push| C2
EW -->|SSE push| C3
EW -->|SSE push| C4
Event Collection and Push
After the client issues a POST request, the connection stays open. Each time the kernel triggers an anomaly event, HUATUO Agent filters and wraps it, then writes it immediately to all matching SSE streams. No client polling is required.
sequenceDiagram
participant C as Client
participant EW as EventsWatch
participant T as Tracer Layer
participant K as Linux Kernel
C->>EW: POST /v1/events/watch {"filters": {...}}
EW-->>C: 200 OK (Content-Type: text/event-stream)
loop SSE long-lived connection
K->>T: Kernel event triggered (memory_oom_kill / hungtask / softlockup ...)
T->>EW: Report raw event
EW->>EW: Apply filter
alt Filter matched
EW-->>C: data: {CloudEvents JSON}\n\n
else No match
note over EW: Discard, do not push
end
EW-->>C: : ping (keepalive, configured interval)
end
Event Processing Pipeline
From kernel event generation to client delivery, three stages are involved: collection, filtering, and wrapping. End-to-end latency is under 1 second.
flowchart LR
A([Kernel anomaly triggered]) --> B["Tracer collection\neBPF / Kprobe"]
B --> C{Filter matched?}
C -- No --> D([Discard])
C -- Yes --> E["Wrap as CloudEvents 1.0\nid / source / time / data"]
E --> F[Write to SSE stream]
F --> G([Push to subscribers])
🌟 Stay Connected
4 - Profiling
Flame Graph Formats
In profiling, collapsed and flamegraph are the two most common formats, corresponding to the “raw data” and “visual view” layers respectively.
Collapsed Format
Standard Syntax and Format
The collapsed format (also called folded stacks) was defined by Brendan Gregg and serves as the raw text input format for flame graphs. Each line represents a unique call stack and its sample count.
Basic rule:
frame1;frame2;frame3;...;frameN COUNT
| Component | Description |
|---|---|
frame1 |
Stack bottom (entry/root frame), e.g. main, start_thread |
; |
Frame separator (semicolon) |
frameN |
Stack top (currently executing frame, i.e. the sampled point) |
COUNT |
Sample count (integer), separated from the stack frames by a space |
Format details:
- One unique call stack per line; samples with the same stack path have their counts merged
- Frame order: left to right is root → leaf (call chain direction)
- Blank lines and lines starting with
#are treated as comments and ignored during parsing - The semantics of COUNT depend on the analysis mode: for CPU sampling it is the number of samples, for memory allocation it is the number of bytes allocated, for lock analysis it is the contention time in milliseconds
Frame Name Normalization
Collapsed/folded uses ; as the frame separator and has no standard escape
syntax. To prevent symbol names from being split into extra frames, HUATUO:
- Replaces
;in frame names with: - Replaces carriage returns and line feeds with spaces so each stack occupies one line
- Applies these changes only to collapsed/folded output; internal symbols and structured formats retain the original names
For example, generic::<[u8; 7]> is written as generic::<[u8: 7]>.
This conversion is lossy: symbols that differ only by ; and : are merged. Use
pprof or another structured format when exact symbol names must be preserved.
Extended specification:
Some profiling tools (e.g. async-profiler) add frame type annotations on top of the standard format to identify the runtime category of a frame:
frameName_{type} COUNT
| Annotation | Meaning | Description |
|---|---|---|
_[j] |
JIT compiled Java | Java method after JIT compilation |
_[i] |
Interpreted Java | Java method executed by the interpreter |
_[k] |
Kernel | Kernel-mode frame |
_[n] |
Native C/C++ | Native C/C++ frame |
_[t] |
Thread | Thread frame |
Additionally, some tools support a weighted collapsed format for differential flame graphs:
frame1;frame2;frameN WEIGHT
Where WEIGHT is a floating-point number representing the weight of the stack rather than a simple count.
Sample Examples
CPU profiling example (data from the async-profiler official documentation):
FileConverter.main;FileConverter.convertFile;FileConverter.saveResult 21
FileConverter.main;FileConverter.convertFile;FileConverter.saveResult;java/io/DataOutputStream.writeInt 1
FileConverter.main;FileConverter.convertFile;FileConverter.saveResult;java/io/DataOutputStream.writeInt;java/io/ByteArrayOutputStream.write 5
FileConverter.main;FileConverter.convertFile;FileConverter.saveResult;java/io/DataOutputStream.writeUTF;java/io/DataOutputStream.writeUTF 12
FileConverter.main;FileConverter.convertFile;FileConverter.saveResult;java/io/DataOutputStream.writeUTF;java/io/DataOutputStream.writeUTF;java/lang/String.length 3
FileConverter.main;FileConverter.convertFile;FileConverter.saveResult;java/io/DataOutputStream.writeUTF;java/io/DataOutputStream.writeUTF;java/io/DataOutputStream.write 6
start_thread;thread_native_entry;Thread::call_run;VMThread::run;VMThread::inner_execute;VMThread::evaluate_operation;VM_Operation::evaluate;VM_GenCollectForAllocation::doit;GenCollectedHeap::satisfy_failed_allocation;GenCollectedHeap::do_collection;GenCollectedHeap::collect_generation;DefNewGeneration::collect;DefNewGeneration::FastEvacuateFollowersClosure::do_void 12
Example with frame type annotations (async-profiler extension):
Main.run_[j];Service.process_[j];DAO.query_[j];mysql_real_query_[n] 45
Main.run_[j];Service.process_[j];DAO.query_[j];recv_[k] 18
Core Use Cases
| Use Case | Description |
|---|---|
| Flame graph generation | Standard input format for visualization tools like flamegraph.pl and inferno |
| Differential analysis | Compare two collapsed files to produce a red-blue differential flame graph for detecting performance regressions |
| Programmatic processing | Plain text format suitable for custom aggregation and filtering with awk, sed, Python, etc. |
| Cross-tool interoperability | Universal standard defined by Brendan Gregg; supported by virtually all flame graph toolchains |
| Long-term storage | Compact text format suitable for archiving and version comparison |
| CI/CD integration | Enables automated collection, diffing, and threshold-based regression detection in pipelines |
Generation command example:
# Using async-profiler as an example
asprof -d 30 -f profile.collapsed -o collapsed <PID>
Flamegraph Format
Standard Syntax and Format
The flamegraph format is a self-contained HTML file with embedded SVG visualization and JavaScript interaction logic, which can be opened directly in a browser.
Structural composition:
flamegraph.html
├── HTML skeleton + CSS styles
├── SVG flame graph body
│ ├── <g> rectangle block for each frame
│ │ ├── <title> frame name + sample count/percentage
│ │ └── <rect> position, width, height, color
│ └── ...
├── JavaScript interaction logic
│ ├── Click to zoom (zoom into subtree)
│ ├── Search & highlight
│ ├── Tooltip on hover
│ └── Reset zoom
└── Metadata (title, total samples, etc.)
Visual encoding rules:
| Dimension | Encoding Meaning |
|---|---|
| X axis | Call stack frames sorted alphabetically (not a timeline); width proportional to sample count |
| Y axis | Call stack depth; bottom is the root frame, top is the leaf frame |
| Frame width | Proportion of samples where this frame appears in the stack; wider frames consume more resources |
| Frame color | Identifies the frame type (see table below) |
Frame color specification (based on async-profiler):
Note: Flame graph color schemes are not a cross-tool standard. The original
flamegraph.plby Brendan Gregg uses random warm tones with no semantic meaning;perf/bpftracetypically colors by DSO or uses random colors; async-profiler colors by frame type semantics. The following is the async-profiler color specification:
| Color | Frame Type | Description |
|---|---|---|
| 🟢 Green | Java (interpreted) | Java method executed by the interpreter |
| 🟡 Yellow/Orange | Java (JIT compiled) | Java method after JIT compilation |
| 🔴 Red | C/C++ (native) | Native C/C++ code |
| 🔵 Blue | Kernel | Kernel-mode code |
| ⬜ Gray | Other/Unknown | Other types or unknown frames |
Extended features (based on async-profiler):
- Icicle Graph: Displays the call chain top-down (root at the top), which better suits top-down reading habits. Toggle via the
--reverseoption or the Reverse button in the browser - Multi-thread view: Call stacks from different threads are displayed side by side at the root level
- Search highlighting: Matching frames are highlighted in purple; non-matching frames are dimmed
- Sample info tooltip: Hover to display frame name, sample count, and percentage of total samples
- Cutoff frames: Frames marked as
[...]indicate stack truncation (e.g. due to stack depth limits)
Sample Examples
Generation command example:
# Using async-profiler as an example
asprof -d 30 -f flamegraph.html <PID>
Interactive operations:
- Click a frame: Zoom to make the frame full-width, showing only its subtree
- Search box: Enter a keyword; matching frames are highlighted
- Hover: Display frame name, sample count, and percentage
- Reset Zoom: Restore the global view
Core Use Cases
| Use Case | Description |
|---|---|
| Hotspot identification | Visually identify the widest frame blocks to quickly find the code paths consuming the most CPU/memory |
| Root cause analysis | Trace upward from leaf frames to understand the call chain context of resource consumption |
| Team collaboration | HTML files can be shared directly; viewable in a browser with no additional tools required |
| Optimization verification | Generate flame graphs before and after optimization; compare frame width changes to verify effectiveness |
| Non-specialist friendly | Visual form is easier to understand for non-performance engineers, facilitating cross-team communication |
Format Comparison
| Dimension | Collapsed | Flamegraph |
|---|---|---|
| Format type | Plain text | HTML + SVG |
| Human readability | Medium (requires understanding stack frame syntax) | High (visual, intuitive) |
| Machine readability | High (easy to parse, easy to diff) | Low (requires parsing HTML/SVG) |
| Interactivity | None | Supports zoom, search, tooltip |
| File size | Very small (KB scale) | Larger (hundreds of KB to MB scale) |
| Toolchain dependency | None (plain text) | Browser |
| Differential analysis | Natively supported (diff two files) | Requires conversion to collapsed first |
| Typical use case | Programmatic processing, CI comparison, archiving | Manual analysis, team sharing, presentation |
Typical workflow:
Collect ──► collapsed ──► flamegraph.html (manual analysis)
│
├──► Differential flame graph (regression detection)
├──► Custom aggregation scripts
└──► Archive storage
5 - Network Drop Monitoring
Overview
dropwatch observes software drops through tracepoint/skb/kfree_skb and hardware drops reported by capable drivers through raw_tracepoint/devlink_trap_report. It outputs protocol fields, the IP tuple, network device, drop reason, and kernel stack.
dropwatch supports kernel-side filtering based on tcpdump-style filter expressions. The filter logic is compiled into eBPF bytecode at load time by the built-in pure-Go pcap compiler internal/pcapfilter. Filtering is performed entirely in kernel mode — only matching packets are reported to user space, reducing performance impact on the host.
In addition, dropwatch supports device whitelist/blacklist filtering, global per-second rate limiting, and integration with huatuo-bamai to store drop events in Elasticsearch for long-term analysis.
Scenarios
1. Kubernetes Cloud-Native Network Drop Diagnosis
In scenarios such as container migration, frequent Pod restarts, and Service port conflicts, dropwatch captures kfree_skb events in real time and correlates them with specific containers to quickly identify the root cause of packet drops. Combined with --filter "tcp and port <service-port>" to filter specific business traffic, the mean time to root cause is reduced from hours to minutes.
2. Network Performance Spike Analysis
For intermittent spikes in network latency or drops in throughput, dropwatch collects drop events and, together with the kernel call stack, identifies the specific kernel function where the drop occurred (e.g. tcp_v4_rcv, ip_output). This helps distinguish whether the cause is a firewall drop, routing failure, buffer overflow, or other reasons.
3. Multi-Tenant Network Isolation Troubleshooting
In container environments that share network namespaces or veth devices, use --device to filter by network device and --filter to filter by protocol. This precisely captures drop events for the target container, preventing other tenants’ traffic from interfering with the diagnosis.
4. Observability Platform Integration
Use --output-storage to send drop events to huatuo-bamai, which stores them in Elasticsearch for multi-dimensional correlation with metrics and logs. Overlay drop events on a Grafana timeline, aligned with application error rates and latency curves, to correlate kernel drops with application anomalies precisely.
Usage
1. Filter Expressions
Filter expressions use tcpdump syntax. The built-in pure-Go pcap compiler internal/pcapfilter compiles them into eBPF bytecode at load time. Filtering is performed entirely in kernel mode, reducing host impact — only matching packets are reported to user space.
1.1 Supported Expressions
internal/pcapfilter supports a subset of the standard tcpdump syntax. The following primitives are reliable:
Protocols
ip ip6 tcp udp icmp icmp6 igmp pim esp ah vrrp arp rarp
ip proto tcp ip6 proto udp (protocol names only; numeric protocol numbers not supported)
Host addresses
host 10.0.0.1
src host 10.0.0.1
dst host 10.0.0.1
Ports
port 80
src port 443
dst port 8080
Networks (CIDR)
net 10.0.0.0/8
src net 192.168.1.0/24
dst net 172.16.0.0/12
Multicast and Ethernet addresses
ip multicast ip6 multicast multicast ether multicast
ether host 00:11:22:33:44:55
Boolean operators and grouping
tcp and port 80
tcp or udp
not arp
tcp and (port 80 or port 443)
ip and src net 192.168.1.0/24 and tcp dst port 3306
1.2 Unsupported Expressions
The following expressions are not supported. Using them causes compilation failures or incorrect match results:
| Expression | Reason |
|---|---|
tcp[tcpflags] & tcp-syn != 0, ip[8], tcp[0:4] |
Byte-offset expressions (proto[offset:size]) not implemented |
ip proto 6, ip6 proto 17 |
Numeric protocol numbers not supported; use names (e.g. ip proto tcp) |
ether proto 0x0800 |
Hex EtherType not supported; use names (e.g. ether proto ip) |
sctp |
Keyword not recognized |
portrange 80-90, tcp portrange 1-100 |
Port ranges not supported |
less N, greater N |
Packet-length filtering not supported |
ip broadcast, ether broadcast |
Broadcast matching not supported |
vlan, mpls, pppoes |
Tunnel/encapsulation keywords not supported |
gateway |
Not supported |
1.3 Examples
# Monitor all TCP drops (default — reliable in both L2 and L3 contexts)
--filter "tcp"
# TCP and UDP
--filter "tcp or udp"
# Specific destination host (applies to both TCP and UDP)
--filter "dst host 10.0.0.1"
# Specific port
--filter "tcp and port 443"
# Exclude a noisy host
--filter "tcp and not host 169.254.169.254"
# Specific subnet + specific port
--filter "src net 192.168.1.0/24 and tcp dst port 3306"
# Monitor non-TCP drops (UDP and ICMP only — avoid "not tcp", which captures unknown L3 events)
--filter "udp or icmp"
# Monitor ARP drops only (effective only in L2 context; never matches at L3)
--filter "arp"
--filter "ip"/--filter "ip6"now correctly match the corresponding IP protocol family (L2 by EtherType, L3 by version nibble). If you only care about a specific transport layer or host, prefer the more precisetcp,udp,host, orip proto <name>.
2. Running dropwatch
dropwatch [flags]
| Flag | Default | Description |
|---|---|---|
--bpf-path <path> |
required | Path to the dropwatch eBPF object file |
--filter <expr> |
(none) | tcpdump-style filter expression |
--device <names> |
(none) | Device whitelist: only collect drops from these devices; comma-separated (e.g. eth0,eth1) |
--device-excluded <names> |
(none) | Device blacklist: exclude drops from these devices; mutually exclusive with --device |
--duration <n> |
0 | Stop after N seconds (0 = run until Ctrl-C) |
--output <json|text> |
text |
Output format; ignored when --output-storage is set |
--output-storage <path> |
(none) | Send events to huatuo-bamai via Unix socket |
--task-id <id> |
(none) | Task ID for this session; typically used with --output-storage |
--max-events-per-second <n> |
0 | Global rate limit in events/sec (0 = unlimited); applied after --device / --filter |
--filter and device filtering are orthogonal; when both are specified, both apply (AND semantics). If neither --device nor --device-excluded is specified, all devices are collected. --device and --device-excluded are mutually exclusive; whitelist mode drops SKBs without a net_device, while blacklist mode passes them.
At startup, dropwatch detects devlink:devlink_trap_report. When supported, it loads both software and hardware drop probes. Otherwise, it logs a warning and loads only the software drop probe. Hardware collection also requires a driver that registers devlink drop traps and a target trap whose action is trap. With action drop, hardware sends no packet copy to the CPU, so dropwatch cannot inspect it.
devlink Hardware Drop Detection
dropwatch requires no additional startup flags. Before using hardware drop detection, verify that the kernel, driver, and target trap meet the requirements:
# 1. Verify that the kernel provides the devlink trap tracepoint
test -e /sys/kernel/tracing/events/devlink/devlink_trap_report/id || \
test -e /sys/kernel/debug/tracing/events/devlink/devlink_trap_report/id
# 2. List devlink devices and traps registered by the driver
sudo devlink dev show
sudo devlink trap show <bus/device>
# 3. Enable packet reporting for the target DROP trap
sudo devlink trap set <bus/device> trap <trap-name> action trap
# 4. Start dropwatch and display only hardware drops
sudo dropwatch --bpf-path bpf/net_dropwatch.o --output json 2>/dev/null | \
jq -c 'select(.drop_source == "hardware")'
<bus/device> is the device identifier returned by devlink dev show, such as pci/0000:03:00.0. After diagnosis, restore the trap to its previous action.
This capability collects only packets that the driver reports through DEVLINK_TRAP_TYPE_DROP. It does not capture all hardware packets and does not replace NIC hardware-drop counters. Drops such as hardware queue overflows are visible only when the driver implements and reports them as devlink drop traps. Traps of type exception or control are not reported as drop events.
--filter, --device, --device-excluded, and --max-events-per-second apply to both software and hardware events. Text output formats a hardware reason as reason=<group>/<trap> drop_source=hardware. JSON output uses the separate drop_reason_group, drop_reason, and drop_source fields.
tcpshark --with-dropwatch shares source classification and reason resolution and automatically enables available hardware capture. A successful correlation preserves the same drop_source, drop_reason, and drop_reason_group semantics. See retransmission correlation.
Examples
# Text output, monitor TCP drops on all devices
sudo dropwatch --bpf-path bpf/net_dropwatch.o --filter "tcp"
# Monitor drops on eth0 only
sudo dropwatch --bpf-path bpf/net_dropwatch.o --device eth0 --output json
# Exclude loopback
sudo dropwatch --bpf-path bpf/net_dropwatch.o --device-excluded lo --output json
# Combine device and protocol filters
sudo dropwatch --bpf-path bpf/net_dropwatch.o --device eth0 --filter "tcp and port 443" --output json
# Capture for 60 seconds and exit
sudo dropwatch --bpf-path bpf/net_dropwatch.o --filter "tcp and port 443" --duration 60 --output json
# Forward events to a running huatuo-bamai instance
sudo dropwatch --bpf-path bpf/net_dropwatch.o --filter "tcp" --output-storage /var/run/huatuo-toolstream.sock
# Capture 10 seconds of JSON output, excluding events whose stack contains ip_finish_output
sudo dropwatch --output json --duration 10 --bpf-path bpf/net_dropwatch.o | jq -c 'select(.stack | test("ip_finish_output") | not)'
# Capture 10 seconds of JSON output, printing all fields except stack
sudo dropwatch --output json --duration 10 --bpf-path bpf/net_dropwatch.o | jq -c 'del(.stack)'
jq -c compresses each matching event into a single-line JSON, convenient for saving as NDJSON or further pipe processing. test("ip_finish_output") checks whether stack matches the regex; not negates the result, so the command above excludes stacks containing ip_finish_output. Remove | not to keep only those containing ip_finish_output. del(.stack) removes the stack field from the jq output, useful for viewing just the timestamp, device, process, packet_* metadata, and layers protocol fields. For userspace call-stack filtering before storage, configure EventTracing.IssuesList in huatuo-bamai (see Section 4).
3. Event Data Structure
Each drop event is represented as an NDJSON object (types.DropWatchTracing).
| Field | Type | Description |
|---|---|---|
observed_timestamp |
string | UTC userspace receive/format time (RFC3339Nano), not the kernel hook timestamp |
kernel_observed_timestamp |
string | UTC kernel observation time (RFC3339Nano), converted from the raw monotonic clock. |
type |
string | Reserved TCP type; currently unset (1 common, 2 SYN flood, 3/4 listen overflow) |
drop_source |
string | Drop source: software for the kernel network stack or hardware for a devlink DROP trap |
drop_reason |
string | SKB_DROP_REASON_* for software drops; if kernel BTF resolution fails, dropwatch logs a warning and falls back to the numeric value. For hardware drops, this is the devlink trap name |
drop_reason_group |
string | Devlink trap group used to classify hardware drops; omitted for software drops |
drop_location |
string | Hexadecimal kfree_skb call address for software drops; omitted for hardware drops |
source |
string | Event source; tools for standalone dropwatch and events when launched by huatuo-bamai |
comm |
string | Process name at the time of the drop |
pid |
uint64 | Process TGID |
container_id |
string | Container ID (populated by huatuo-bamai resolution, omitempty) |
memory_cgroup_css_addr |
string | Memory cgroup CSS address, used for container resolution |
net_namespace_cookie |
uint64 | Network namespace cookie, used for container resolution |
net_namespace_inum |
uint32 | Network namespace inum, used for container resolution |
netdev_name |
string | Network device name (e.g. eth0) |
netdev_ifindex |
uint32 | Network interface index |
netdev_queue_mapping |
uint32 | TX queue mapping |
netdev_linkstatus |
[]string | Network device link status flags |
packet_skb_addr |
string | SKB address (hexadecimal, omitempty) |
packet_eth_proto |
string | Raw EtherType (hexadecimal, e.g. 0x0800) |
packet_len_bytes |
uint32 | Kernel skb->len snapshot; it is an SKB logical length and may differ from the on-wire frame length |
layers |
object | Layered protocol parse result; missing layers are omitted |
stack |
string | Kernel call stack (newline-separated) |
For hardware events, stack is the kernel call stack at which the driver reports the devlink trap. It does not identify the actual drop location inside the ASIC. Use drop_reason_group, drop_reason, device information, and driver documentation to diagnose hardware drops.
layers uses fixed fields to express the protocol stack, without relying on a separate protocol enumeration:
| Field | Description |
|---|---|
layers.label |
Protocol combination label, e.g. IPv4/TCP, IPv6/UDP, ARP, unknown |
layers.ether |
L2 fields when a real Ethernet header is present: saddr, daddr, type, len; len is non-zero only for IEEE 802.3 framing |
layers.ipv4 |
IPv4 fields: version, ihl, tos, len, id, flags, frag_offset, ttl, protocol, checksum, saddr, daddr |
layers.ipv6 |
IPv6 fields: version, traffic_class, flow_label, len, next_header, hop_limit, saddr, daddr |
layers.tcp |
TCP fields: sport, dport, seq, ack_seq, data_offset, window, checksum, urgent, sk_state |
layers.udp |
UDP fields: sport, dport, len, checksum |
layers.icmp |
ICMP/ICMPv6 fields: type, code, checksum, id, seq; ICMPv6 echo requests/replies include their identifier and sequence number, while other ICMPv6 messages omit them |
layers.arp |
ARP fields: addr_type, protocol, hw_address_size, prot_address_size, operation, sender_mac, sender_ip, target_mac, target_ip |
For ICMPv6 Echo Request/Reply (types 128/129), id and seq are decoded only
when all four Echo bytes were captured. A truncated Echo header retains the
common ICMPv6 fields with zero ID/Seq. If the common header is also truncated,
the decoded IPv6 layer is retained and layers.icmp is absent. Other ICMPv6
message types do not populate the Echo fields.
JSON omits each zero-valued id or seq independently. Text omits both only
when both are zero. A missing output field therefore cannot distinguish an
absent or truncated Echo header from a valid zero value.
4. Integration with huatuo-bamai
huatuo-bamai launches dropwatch as a subprocess and uses --output-storage to send events to the built-in processing pipeline, which ultimately stores them in Elasticsearch. Typical parameters:
dropwatch \
--bpf-path <CoreBpfDir>/net_dropwatch.o \
--output-storage /var/run/huatuo-toolstream.sock \
--filter "tcp"
4.1 Configuration Reference (huatuo-bamai.conf)
[EventTracing]
# Optional call-stack filters. dropwatch discards events whose stack matches a configured regex.
# Default: []
IssuesList = []
[EventTracing.Dropwatch]
# Tcpdump filter for standalone dropwatch.
# Default: "tcp"
Filter = "tcp"
# Forwarded to dropwatch --max-events-per-second.
# Default: 100
MaxEventsPerSecond = 100
[EventTracing.TCPRetransmit]
# Run tcpshark with a private embedded dropwatch source.
EnableDropwatch = false
Standalone dropwatch always emits raw DropWatchTracing events. Local TCP retransmission correlation loads a separate net_dropwatch.o, uses EventTracing.TCPRetransmit.Filter for both inputs, and emits only finalized TCPRetransmitTracing results. The two modes may run together; embedded drops are never stored as duplicate raw events.
4.2 Noise Filtering
No call-stack noise rule is enabled by default. When EventTracing.IssuesList is configured, huatuo-bamai discards matching events. The following patterns are possible operator-configured filters; validate them against the local kernel and workload before enabling them:
| Pattern | Stack Frame Prefix | Reason |
|---|---|---|
| ARP/neighbor table expiry | neigh_invalidate/ |
Neighbor table entry expiration cleanup; does not affect any active data flow. Remove the rule from EventTracing.IssuesList to disable this filter. |
| bnxt NIC TX completion | bnxt_tx_int/ or __bnxt_tx_int/ |
The Broadcom bnxt NIC driver calls kfree_skb to release SKBs after DMA transmit completion; this is normal behavior, not a drop. |
Closing
6 - TCP Retransmission Tracing
Overview
tcpshark --mode retransmit observes TCP retransmission-related kernel activity through the tcp/tcp_retransmit_skb and tcp/tcp_retransmit_synack tracepoints. It can also observe the tcp_send_loss_probe kprobe when TLP collection is explicitly enabled. Depending on the event type, an event can include the IP 4-tuple, TCP state, congestion-control state, retransmission counters, sequence information, and socket metadata used for container resolution.
The userspace classifier derives a connection phase and a reason label from the event type, sk_state, ca_state, and reorder counters. These labels are operational heuristics, not packet-loss root-cause proof.
Filter expressions are compiled at load time by internal/pcapfilter and run in the kernel. The SKB, SYN-ACK, and TLP hooks always evaluate the expression against the same synthetic L3 TCP packet, with or without local correlation. Protocol, address, network, and port conditions are supported. Ethernet addresses, payload, real packet lengths, IP/TCP options, and raw byte-offset expressions are not available. Safe ethertype checks such as ether proto ip are rewritten for L3. A pair of IPv4-mapped IPv6 socket addresses is filtered as IPv4; the raw perf record remains AF_INET6 and userspace normalizes the addresses before matching. In local correlation mode, tcpshark also applies the exact same expression to embedded dropwatch. Use a direction-symmetric expression when reverse ACK or SYN-ACK evidence must remain in scope.
Scenarios
1. TCP Network Quality and Retransmission Diagnosis
Continuously observe RTO, fast retransmission, reorder-prone retransmission, and TLP events to identify abnormal retransmissions during connection establishment, data transfer, and connection teardown. These signals help investigate packet loss, congestion, reordering, and peer reachability problems.
2. Kubernetes Container Network Troubleshooting
Use the container ID, network namespace, and socket cgroup metadata to identify the workload experiencing retransmissions. Apply --filter "tcp and port <service-port>" to focus on a specific service and reduce interference from other host connections.
3. Application Latency and Throughput Anomaly Analysis
Align TCP retransmission events with application latency, error-rate, and throughput timelines. This helps determine whether RTOs or repeated retransmissions coincide with service degradation and distinguish slow application processing from underlying network problems.
4. Locating Packet Loss with dropwatch Correlation
Run tcpshark in local correlation mode to correlate retransmissions with packet drops in the same process. Matching checks the network namespace, tuple direction, TCP sequence or ACK evidence, and kernel monotonic ordering. A strict match identifies an observed drop, with its software or hardware source taken from the record. A no-match remains unknown because source readiness does not establish that the retransmission’s earlier causal history was observed.
Usage
1. Running tcpshark
tcpshark --mode retransmit [flags]
| Flag | Default | Description |
|---|---|---|
--mode retransmit |
required | Select TCP retransmission tracing mode. |
--enable-tlp, --tlp |
disabled | Also attach tcp_send_loss_probe and emit TLP events. |
--bpf-path <path> |
required without correlation | Path to one tcp_retransmit.o file. |
--bpf-path-dir <dir> |
required with correlation | Directory containing tcp_retransmit.o and net_dropwatch.o. |
--with-dropwatch |
disabled | Load embedded dropwatch and correlate it with retransmissions. |
--filter <expr> |
(none) | L3-compatible tcpdump-style filter for all retransmit hooks; also shared with embedded dropwatch in local mode; see §2. |
--device <names> |
(none) | Comma-separated interface allowlist, as in dropwatch; filters only embedded dropwatch and requires --with-dropwatch. |
--device-excluded <names> |
(none) | Interface denylist, mutually exclusive with --device; requires --with-dropwatch. |
--duration <n> |
0 | Stop after N seconds (0 = run until Ctrl-C). |
--max-events-per-second <n> |
0 | BPF-side event rate limit; 0 means unlimited. |
--output <json|text> |
text |
Output format; ignored when --output-storage is set. |
--output-storage <path> |
(none) | Send events to huatuo-bamai over a Unix socket. |
--task-id <id> |
(none) | Task ID for the toolstream session; requires --output-storage. |
When both --output and --output-storage are explicitly specified, --output is ignored and a warning is printed.
1.1 Examples
# Text output for all retransmission-related events
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o
# NDJSON output
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o --output json
# BPF-side filter for regular retransmitted SKBs to one destination host and port
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o --filter "dst host 10.0.0.1 and dst port 443"
# Correlate locally; both BPF inputs use the same direction-symmetric filter
sudo tcpshark --mode retransmit --with-dropwatch --bpf-path-dir bpf \
--filter "tcp and port 443"
# Include Tail Loss Probe events (disabled by default)
sudo tcpshark --mode retransmit --enable-tlp --bpf-path bpf/tcp_retransmit.o
# Emit at most 100 events/second; overflow prints a rate limit hit log
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o \
--max-events-per-second 100
# Filter all formatted event types to destination port 443 in userspace
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o --output json \
| jq -c 'select(.tcp_dport == 443)'
# Keep only events classified as RTO for 60 seconds
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o --duration 60 --output json \
| jq -c 'select(.tcp_reason == "RTO")'
# Forward events to a running huatuo-bamai instance
sudo tcpshark --mode retransmit --bpf-path bpf/tcp_retransmit.o \
--output-storage /var/run/huatuo-toolstream.sock
jq -c emits compact single-line JSON, which is convenient for NDJSON files and downstream pipelines.
1.2 Integration with huatuo-bamai
tcpshark uses the same --output-storage and toolstream flow as dropwatch. For the common storage workflow, refer to the dropwatch documentation. TCP retransmission tracing adds the following configuration:
[EventTracing.TCPRetransmit]
# Used by tcpshark in both modes. Default: empty.
Filter = ""
# Forwarded as tcpshark --enable-tlp. Default: false.
EnableTLP = false
# Run tcpshark with an embedded dropwatch source. Default: false.
EnableDropwatch = false
# Forwarded as tcpshark --max-events-per-second. Default: 100; 0 disables it.
MaxEventsPerSecond = 100
EventTracing.TCPRetransmit.Filter controls retransmission collection in both
modes. With local correlation disabled, an empty value passes no --filter
flag. With local correlation enabled, the normalized expression is passed to
both tcpshark inputs and an empty value becomes tcp.
EventTracing.Dropwatch.Filter remains independent and controls only
standalone dropwatch.
The tcp_retransmit tracer is in the global BlackList by default. Remove it
from the list and restart huatuo-bamai to enable the tracer. Standalone
dropwatch may remain blacklisted because local correlation owns a private
dropwatch source.
2. Filter Expressions
tcpshark uses the same tcpdump-style filter expressions as dropwatch. For complete syntax, limitations, and additional examples, refer to the dropwatch documentation.
# Select one destination host and port
--filter "dst host 10.0.0.1 and dst port 443"
# Select traffic in both directions between two networks
--filter "(src net 10.10.0.0/16 and dst net 10.20.0.0/16) or (src net 10.20.0.0/16 and dst net 10.10.0.0/16)"
In local mode the same expression must cover both traffic directions. A directional selector can exclude reverse ACK or SYN-ACK drop evidence and make the result less useful.
Local mode rejects Ethernet-address primitives such as
ether host 02:00:00:00:00:01. Ethertype primitives such asether proto ipandether proto ip6are supported because they can be rewritten as raw-IP version checks.
3. Event Data Structure
Each event is an NDJSON object (types.TCPRetransmitTracing). Fields tagged with omitempty are absent when their value is empty or zero.
| Field | Type | Description |
|---|---|---|
observed_timestamp |
string | UTC userspace receive/format time (RFC3339Nano), not the kernel hook timestamp. |
kernel_observed_timestamp |
string | UTC kernel observation time (RFC3339Nano), converted from the raw monotonic clock. |
comm |
string | Current kernel execution-context command, not necessarily the socket-owning process. |
pid |
uint64 | Current execution-context TGID, not necessarily the socket owner’s TGID. |
container_id |
string | Container ID when resolved by huatuo-bamai; see §3.2. |
memory_cgroup_css_addr |
string | Socket memory-cgroup CSS address in hexadecimal form, used for container resolution. |
net_namespace_cookie |
uint64 | Socket network-namespace cookie used for container resolution. |
net_namespace_inum |
uint32 | Socket network namespace inum used for container resolution. |
tcp_saddr |
string | Source IP address. |
tcp_daddr |
string | Destination IP address. |
tcp_sport |
uint16 | Source port. |
tcp_dport |
uint16 | Destination port. |
tcp_state |
string | TCP socket state, such as ESTABLISHED, SYN_SENT, or NEW_SYN_RECV. |
phase |
string | Classifier output: connect, data, or close. |
tcp_reason |
string | Classifier output: RTO, fast_retransmit, TLP, or unknown. |
event_type |
string | tcp_retransmit_skb, tcp_retransmit_synack, or tcp_send_loss_probe. |
ca_state |
uint8 | Congestion-control state: 0=Open, 1=Disorder, 2=CWR, 3=Recovery, 4=Loss. |
icsk_retransmits |
uint8 | Current retransmission counter snapshot. |
icsk_pending |
uint8 | Raw pending timer state from inet_connection_sock; see the value table below. |
reord_seen |
uint32 | Cumulative flow reorder counter. |
dsack_dups |
uint32 | Cumulative DSACK duplicate counter. |
tcp_seq |
uint32 | TCP_SKB_CB(skb)->seq for SKB events; snd_nxt for TLP; request snt_isn for SYN-ACK when available. |
tcp_ack_seq |
uint32 | tcp_sk(sk)->rcv_nxt for SKB events; snd_una for TLP; request rcv_nxt for SYN-ACK when available. |
tcp_end_seq |
uint32 | TCP_SKB_CB(skb)->end_seq for SKB events; request snt_isn + 1 for SYN-ACK when available; omitted for TLP. |
tcp_flags |
string | Rendered TCP flag set such as `SYN |
skb_addr |
string | Retransmission-queue SKB pointer in hex; absent for SYN-ACK and TLP events. |
drop_location |
string | Correlation classification: software, hardware, or unknown. Equals drop_source for a match; unlike the dropwatch field, it is not a kernel address. |
drop_source |
string | Matched drop source: software or hardware; an unrecognized ABI source is unknown. Omitted on no-match. |
drop_reason |
string | Same as dropwatch: a BTF-resolved SKB_DROP_REASON_* name for software, a decimal value if unresolved, or NOT_SUPPORTED on older kernels. Hardware drops use the devlink trap name. |
drop_reason_group |
string | Devlink trap group, such as l2_drops, for aggregating hardware reasons. Omitted for software drops and no-matches. |
correlation_reason |
string | One terminal outcome: matched, unsupported, warmup, wait_timeout, queue_full, or interrupted. Omitted when correlation is disabled. |
matched_net_namespace |
bool | A drop on the same TCP flow was observed in the same namespace, independently of packet and time checks. Always true for a strict match; omitted when false. |
drop_perf_status |
object | Cumulative embedded-dropwatch counters for an unmatched result. map_counters_available identifies valid map counters; reader lost_samples remains valid on map errors. |
drop_stack |
string | Matched drop stack; unmatched stacks are not symbolized. |
source |
string | Event source. It is tools when tcpshark runs standalone and events when huatuo-bamai launches it. |
icsk_pending is a timer-state snapshot at the hook, not a stable retransmission-reason enum. TLP classification uses the explicit event_type=tcp_send_loss_probe and does not depend on icsk_pending=5.
| Value | Kernel state | Meaning |
|---|---|---|
0 |
None | No transmit-timer event is currently pending. |
1 |
ICSK_TIME_RETRANS |
Retransmission timeout timer (RTO). |
2 |
ICSK_TIME_DACK |
Delayed ACK; modern kernels keep this state in icsk_ack.pending and use a separate delayed-ACK timer, so it normally does not appear in icsk_pending. |
3 |
ICSK_TIME_PROBE0 |
Zero-window probe timer. |
4 |
Version-dependent | Current mainline kernels no longer define this value; older kernels used it for Early Retransmit, and still older kernels used it for Keepalive. |
5 |
ICSK_TIME_LOSS_PROBE |
Tail Loss Probe (TLP) timer. |
6 |
ICSK_TIME_REO_TIMEOUT |
Reordering timeout, primarily used by RACK loss detection. |
3.1 Text Output Format
Text retains its terminal-friendly layout while covering the same event variables as JSON. Optional variables appear only when non-zero or non-empty, and string values are not JSON-quoted or escaped. For compatibility with the original text format, state, skb, seq, end, ack, flags, ca, retrans, and reason correspond to the JSON fields tcp_state, skb_addr, tcp_seq, tcp_end_seq, tcp_ack_seq, tcp_flags, ca_state, icsk_retransmits, and correlation_reason, respectively.
<timestamp> [<phase>/<tcp_reason>] <saddr>:<sport> > <daddr>:<dport> state=<STATE> event_type=<TYPE> [kernel_observed_timestamp=<UTC>] [SYNACK] [skb=<ADDR>] seq=<N> [end=<N>] ack=<N> [flags=<FLAGS>] pid=<N> comm=<COMM> ca=<N> retrans=<N> icsk_pending=<N> [reord_seen=<N>] [dsack_dups=<N>] [container_id=<ID>] [memory_cgroup_css_addr=<ADDR>] [net_namespace_cookie=<N>] [net_namespace_inum=<N>] [drop_location=<LOCATION>] [drop_source=<SOURCE>] [drop_reason=<REASON>] [drop_reason_group=<GROUP>] [reason=<REASON>] [matched_net_namespace=true] [dropwatch_map_counters_available=<true|false> dropwatch_perf_lost=<N> dropwatch_lost_samples=<N> dropwatch_rate_limited=<N>] [source=<SOURCE>]
Example:
2026-07-23T02:14:40.304775546Z [data/RTO] 127.0.0.1:19996 > 127.0.0.1:42128 state=ESTABLISHED event_type=tcp_retransmit_skb kernel_observed_timestamp=2026-07-23T02:14:40.304Z skb=0xffff931c14fdf800 seq=3154974646 end=3154991030 ack=948393597 flags=ACK|PSH pid=1420 comm=kube-apiserver ca=4 retrans=4 icsk_pending=0 net_namespace_inum=4026531992
The pid and comm in this example describe the execution context in which the hook ran; use container_id and socket metadata for workload attribution.
A non-empty drop_stack is rendered as indented lines after the event line,
not as an inline drop_stack= token.
3.2 Container ID Resolution
tcpshark cannot access the Pod manager directly. In standalone output, container_id is normally absent, while socket memcg and network-namespace metadata are still emitted when available. In huatuo-bamai mode, an empty container_id is resolved in this order: memory_cgroup_css_addr, net_namespace_cookie, then net_namespace_inum.
If all lookups miss, the event is still stored without container_id. Do not use pid or comm as a fallback for socket ownership because they describe the hook execution context.
4. Kernel Events and Classification
4.1 Kernel Hook Points
| Hook | Kernel location | What the event means | Data availability |
|---|---|---|---|
tracepoint tcp/tcp_retransmit_skb |
__tcp_retransmit_skb() |
A retransmission was attempted for a retransmission-queue SKB. The tcpshark event does not retain the kernel transmit result. The SKB is headerless, so sequence fields come from TCP_SKB_CB(skb) and ACK comes from tcp_sk(sk)->rcv_nxt. |
SKB pointer, TCP seq/end_seq/ack/flags, socket state, CA state, timers, and reorder counters. |
tracepoint tcp/tcp_retransmit_synack |
tcp_rtx_synack() |
A passive-open SYN-ACK retransmission was successfully submitted by tcp_rtx_synack(). |
Request-socket addresses and ports; no retransmission SKB pointer or TCP seq/ack. |
kprobe tcp_send_loss_probe |
tcp_send_loss_probe() |
A Tail Loss Probe is being prepared; collected only with --enable-tlp. |
Socket metadata plus snd_nxt/snd_una; no SKB pointer or rendered TCP flags. |
The BPF program uses CO-RE field reads (BPF_CORE_READ and related helpers), so supported kernel layouts do not require rebuilding the C source for each kernel version.
4.2 Connection Phase
The regular-SKB phase is derived from sk_state. SYN-ACK events use a fixed phase in userspace.
The TCP three-way handshake below shows the connect phase and its retransmission hook points:
sequenceDiagram
participant C as Client
participant S as Server
Note over C,S: Initial states: CLOSED / LISTEN
C->>S: ① SYN
Note left of C: SYN_SENT(2)<br/>phase=connect
opt SYN is not acknowledged
C-->>S: SYN retransmission<br/>tcp_retransmit_skb
end
Note right of S: SYN_RECV(3) or NEW_SYN_RECV(12)<br/>phase=connect
S->>C: ② SYN + ACK
opt Final ACK does not arrive
S-->>C: SYN-ACK retransmission<br/>tcp_retransmit_synack
end
C->>S: ③ ACK
Note over C,S: ESTABLISHED(1)<br/>subsequent regular data-SKB events use phase=data
The three solid arrows are the initial handshake packets and do not produce tcpshark events. Only the retransmission paths inside the optional blocks are observed. Active-open SYN retries are reported by tcp_retransmit_skb, while passive-open SYN-ACK retries are reported by tcp_retransmit_synack; both are classified as connect.
The complete phase mapping is:
| Phase | Source state or event | Description |
|---|---|---|
connect |
SYN_SENT(2), SYN_RECV(3), NEW_SYN_RECV(12), or tcp_retransmit_synack |
Connection establishment. |
data |
ESTABLISHED(1) or unrecognized/default states | Data transfer/default classification. |
close |
FIN_WAIT1(4), FIN_WAIT2(5), TIME_WAIT(6), CLOSE_WAIT(8), LAST_ACK(9), CLOSING(11) | Connection teardown. |
4.3 Reason Classification
| Event or condition | Reason | Interpretation |
|---|---|---|
tcp_retransmit_synack |
RTO |
Fixed userspace label for the SYN-ACK retry timer path. |
tcp_send_loss_probe |
TLP |
Fixed userspace label for the optional Tail Loss Probe hook. |
tcp_retransmit_skb, ca_state=4 (Loss) |
RTO |
The socket is in TCP_CA_Loss. |
tcp_retransmit_skb, ca_state=3 (Recovery) |
fast_retransmit |
Recovery-path retransmission. |
tcp_retransmit_skb, ca_state=0..2, connect/close phase |
RTO |
Phase-based fallback used by the current classifier. |
tcp_retransmit_skb, ca_state=0..2, data phase |
unknown |
The available snapshots are insufficient to assign another label. |
The classifier observes socket state at the hook and cannot reconstruct the complete ACK/loss history. Treat tcp_reason as a grouping label rather than a verified root cause.
4.4 Reorder Heuristic
4.5 Operational Guidance
No event type is unconditionally safe to discard. Prefer rate, ratio, and service-impact thresholds over filtering solely by event_type or tcp_reason. For the common huatuo-bamai noise-filtering mechanism, refer to the dropwatch documentation.
| Pattern | Typical priority | Guidance |
|---|---|---|
tcp_reason=RTO |
High | Investigate sustained or service-correlated increases; RTO normally has greater latency impact than Recovery-path retransmission. |
tcp_reason=fast_retransmit |
Medium | Correlate with loss, congestion, and SACK/RACK behavior. |
tcp_reason=TLP |
Context dependent | Optional signal only; confirm that TLP collection was deliberately enabled before using it in alerting. |
event_type=tcp_retransmit_synack |
Usually low per isolated retry | Repeated events can indicate handshake reachability, host egress, firewall, or client/network problems. |
When building alerts, aggregate by service or connection and compare against traffic volume. A small absolute count on a busy host can be benign, while a burst affecting a low-volume critical service can be significant.
5. Correlation with dropwatch
With --with-dropwatch, one tcpshark process owns both perf inputs. A retransmission waits up to 100 ms for a delayed dropwatch delivery. A candidate drop must precede the retransmission by no more than one second in kernel monotonic time. The embedded source never emits raw drop documents; separately enabled standalone dropwatch remains an independent raw-event stream.
For the limitations around out-of-order reads across the two perf streams, the 100 ms arrival window, the one-second causal window, and negative evidence, see Challenges of TCP Retransmission and dropwatch Correlation.
Like standalone dropwatch, the embedded source automatically detects and enables devlink DROP trap capture (HardwareAuto). No additional hardware flag is needed. If the tracepoint is unavailable, it logs a warning and continues software capture. Visibility depends on driver reporting; see the dropwatch hardware requirements.
--device, --device-excluded, --filter, and --max-events-per-second use the same names as dropwatch. The interface allowlist or denylist limits only embedded dropwatch, covering both software and hardware events. The allowlist rejects records without device information; the denylist permits them. Retransmissions still use the shared L3 filter, so device filtering can reduce available drop evidence.
Matched output retains drop_source, drop_reason, and the hardware drop_reason_group. drop_reason describes the observed drop, tcp_reason classifies the retransmission trigger, and correlation_reason identifies how this correlation attempt ended. Independent diagnostic fields describe observation limits. The BTF reason table loads once per session. A load failure logs a warning and falls back to numeric software reasons; hardware trap decoding remains available.
# Use the same interface and traffic options as dropwatch to inspect matched hardware drops
sudo tcpshark --mode retransmit --with-dropwatch --bpf-path-dir bpf \
--device eth0 --filter "tcp and port 443" --output json \
| jq -c 'select(.drop_source == "hardware")'
Hardware drops must meet the same namespace, TCP, and time constraints. Missing namespace or TCP evidence leaves a retransmission unmatched; no-match does not establish hardware loss. A hardware drop_stack is the driver’s trap-reporting kernel stack, not a location inside the ASIC.
An unrecognized ABI source produces unknown for both drop_source and drop_location, even when packet evidence matches. The source is never inferred from a reason or stack. Such a match still reports correlation_reason=matched.
5.1 Correlation Results
| Result | Required evidence | Output |
|---|---|---|
| Outbound segment match | Same network namespace, family, direction, tuple, monotonic ordering, and overlapping SYN/data/FIN sequence range. | correlation_reason=matched, with the drop source, reason, and drop_stack. |
| Reverse ACK match | Reverse tuple in the same namespace, ACK flag set, monotonic ordering, and ACK covering the retransmitted sequence end. | correlation_reason=matched, with the drop source, reason, and drop_stack. |
| No strict match | Missing local evidence does not establish where the packet was lost. | drop_location=unknown, one terminal correlation_reason, and independent diagnostics. |
There is no tuple-only, SKB-pointer-only, cross-namespace, or ambiguous positive match. The matched_net_namespace diagnostic records a same-namespace drop on the same flow in either direction before packet and time checks. It can be true without a strict match; correlation_reason=matched remains the indication of full correlation. A false or absent flag means no namespace match was observed, not that a different namespace was confirmed. A matched drop is consumed once, while later drops on the same connection remain available. Stack symbolization runs only after a match.
5.2 Correlation Outcome
Each finalized event has exactly one correlation_reason:
| Value | Meaning |
|---|---|
matched |
Strict drop evidence matched the retransmission, including matches whose drop source is unknown. |
unsupported |
The event lacks the type, namespace, time, or sequence evidence required by the matching rules. |
warmup |
The 100 ms wait elapsed without a strict match, and the retransmission predates embedded-source readiness. |
wait_timeout |
The 100 ms wait elapsed without a strict match, and the retransmission occurred at or after embedded-source readiness. This is a correlation wait, not TCP’s RTO. |
queue_full |
The bounded retransmission wait queue evicted this event before its deadline. |
interrupted |
The correlation loop exited before this event’s wait deadline. This includes normal shutdown and failures. |
Expired entries are finalized before matching or checking capacity. Shutdown uses one time snapshot: expired waits receive warmup or wait_timeout; remaining waits receive interrupted. Each event is finalized once and removed from the queue. Drop records rejected during decoding or evicted from the drop cache do not directly finalize a retransmission.
The scalar field replaces the previous reason array; update tcpshark and its receiver together. Text output uses reason=<value>. Correlation-disabled output omits the field.
warmup is selected only for an expired wait when the retransmission’s kernel timestamp predates embedded-source readiness. Retransmissions at or after readiness receive wait_timeout on expiry, including those within the first second. The one-second drop-to-retransmit limit constrains candidate matching; it does not define warmup. Processing delays do not change this classification. Strict matches, unsupported events, queue evictions, and early interruptions retain their own reasons. The outcome describes observation timing; it does not prove why a drop failed to match.
matched_net_namespace remains an independent boolean diagnostic for both matched and unmatched results. It can coexist with loss counters without changing the terminal reason.
At shutdown, tcpshark stops reading dropwatch records still in the perf ring. An unread tail drop could otherwise have matched an interrupted retransmission.
5.3 Dropwatch Perf Status
Each non-empty output batch reads status once, including batches containing only matches. Unmatched events retain the snapshot:
| Field | Meaning |
|---|---|
map_counters_available |
Whether both map counters below are available. False distinguishes unavailable values from observed zeros. |
perf_lost |
Cumulative embedded-dropwatch events the kernel failed to write to the perf stream; valid only when map counters are available. |
lost_samples |
Cumulative samples reported by reader-side PERF_RECORD_LOST records; still valid when map reads fail. Omitted when zero. |
rate_limited |
Cumulative events rejected by the embedded rate limiter; valid only when map counters are available. |
On a status read error, map_counters_available is false. The zero values of perf_lost and rate_limited mean unavailable; the snapshot still retains lost_samples. tcpshark attempts to output the batch before returning the status error. A write failure stops the batch and preserves both errors. Matched events retain matched and their drop metadata without an unmatched status snapshot.
Counters belong to this dropwatch instance and reset on reload. They are sampled separately, not atomically, and cannot prove why an individual retransmission failed to match. Loss and rate limiting never replace or add a correlation reason.
5.4 Requirements and Troubleshooting
| Observation | Checks |
|---|---|
software |
Inspect drop_reason and the matched stack with tuple, direction, sequence, and namespace evidence. |
hardware |
Check the trap group, trap name, and driver documentation; the match remains a correlation based on packet evidence. |
unknown with loss counters |
Narrow the shared filter, increase perf capacity, or adjust the embedded dropwatch rate limit, then capture again. |
correlation_reason=wait_timeout |
No strict candidate arrived within 100 ms; inspect the diagnostic flags and counters. |
correlation_reason=queue_full |
The waiting queue reached capacity; narrow the capture scope. |
correlation_reason=interrupted |
Inspect the collection stop or error; the wait ended early. |
matched_net_namespace=true with an unmatched reason |
A same-flow drop was observed in the same namespace. Check packet and time eligibility; the flag alone does not establish correlation. |
correlation_reason=warmup |
The wait expired for a retransmission that predates source readiness. Capture later retransmissions; this result cannot exclude a drop before source readiness. |
drop_location absent |
Expected in off mode. |
huatuo-bamai passes one normalized EventTracing.TCPRetransmit.Filter value to both local-correlation inputs. Keeping those scopes identical prevents the two sources from observing different traffic, but it does not make a no-match conclusive without a reliable causal-start boundary.
Closing
7 - IRQ Tracing
The standalone command collects irq/softirq stacks from every CPU or one selected CPU and builds a flame graph of the sources (who raised the softirq) and the victims (the tasks directly preempted by the softirq). When ksoftirqd services a softirq, it is the executor rather than a victim and is excluded from victim samples. It is the same tool the huatuo-bamai daemon shells out to from its AutoTracing.IRQTracing tracer; running it directly produces the same result without any storage backend or configuration file.
Build
The normal project build discovers cmd/irqtracing automatically and compiles bpf/irqtracing.c:
make build
The command is written to _output/bin/irqtracing and the BPF object to _output/bpf/irqtracing.o; the object must be present at run time (--bpf-path).
Run
sudo ./_output/bin/irqtracing \
--bpf-path ./_output/bpf/irqtracing.o \
--duration 5 > irqtracing.folded
--target-cpu is optional and defaults to -1, which traces every CPU. The tool attaches only the irq/softirq_raise and irq/softirq_entry tracepoints. All-CPU mode aggregates sources and directly preempted victims across the system; selecting a non-negative CPU keeps only data from that CPU. Both modes collect for --duration seconds (default 3).
--max-events-per-second-per-cpu N optionally limits the combined number of source and victim stack samples collected per second on each traced CPU. Omitting it or setting it to 0 disables rate limiting; when enabled, N must be between 2 and 8589934590. The per-CPU budget is divided as evenly as possible between softirq_raise and softirq_entry; raise receives the extra sample when the value is odd. In all-CPU mode, the system-wide limit scales with the number of traced CPUs.
Local output is written to stdout. --output text is the default and an alias
for collapsed; it emits folded stacks suitable for flame graph tools:
source;source[raiser,NET_RX];raise_softirq_[k] 12
victim;victim[worker(123),NET_RX];work 4
Use --output flamegraph or --output svg to render an SVG flame graph
directly:
sudo ./_output/bin/irqtracing \
--bpf-path ./_output/bpf/irqtracing.o \
--duration 5 \
--output flamegraph > irqtracing.svg
Use --output json when the platform profile payload and drop count are needed:
sudo ./_output/bin/irqtracing \
--bpf-path ./_output/bpf/irqtracing.o \
--duration 5 \
--output json > irqtracing.json
The JSON contains:
{
"flamedata": { "...": "profile tree, null when nothing was collected" },
"nmissed": 1234
}
flamedata is the same profile format the platform consumes (ProfileType irqtracing:irq:count:irq:count). Every stack is rooted at source or victim, followed by a source[comm,VEC] or victim[comm(pid),VEC] label frame, then the user-space frames and the kernel frames suffixed with _[k]. VEC is one of HI, TIMER, NET_TX, NET_RX, BLOCK, IRQ_POLL, TASKLET, SCHED, HRTIMER, RCU, or VEC<n> for unknown vectors.
nmissed is the total number of samples dropped during the collection window. Samples are dropped either by the first-N budgets enabled through --max-events-per-second-per-cpu or because a counts map is full. Either case means the flame graph is incomplete. When the drop count cannot be read, the tool fails instead of writing a result. A non-zero nmissed also produces a warning line on stderr; the JSON field is the persisted contract consumed by the daemon.
--output-storage <socket> sends the JSON payload over Toolstream instead of
writing local output and requires --task-id. An explicitly supplied
--output is ignored in that mode. Operational logs are discarded; local
results go to stdout and failures or drop warnings go to stderr.
The process exits when the duration elapses, when the caller cancels, or when it receives SIGHUP, SIGQUIT, SIGINT, or SIGTERM.
Use from huatuo-bamai
The daemon’s AutoTracing.IRQTracing tracer invokes the irqtracing binary located next to the daemon executable itself (CoreBinDir is derived from the daemon’s own path; in the source build tree that is _output/bin/irqtracing) when its rules detect an irq/softirq spike or sustained high utilization on some CPU, passing --bpf-path <CoreBpfDir>/irqtracing.o, --target-cpu <cpu>, --duration <RunTracingToolTimeout> and --max-events-per-second-per-cpu <MaxEventsPerSecond>. MaxEventsPerSecond defaults to 1000, so raise and entry each default to 500 events/s. The CLI returns its result over Toolstream and the daemon merges it into the saved tracing data; the daemon additionally records rule, trigger_cpu, trace_duration and hit_cpus.