Skip to content

Commit d3366ca

Browse files
danielpaulusDaniel Paulus
andauthored
add basic metrics for Prometheus (#345)
This PR adds basic metrics, exposed with Prometheus. go-ncm accepts one cmd line argument now: --prometheusport=8080 if specified, prometheus metrics will be available at http://0.0.0.0:prometheusport/metrics if not specified, the prometheus endpoint will not be started and not be available. Metrics device_count Type: Gauge Description: Tracks the number of iOS devices currently connected to the system. A gauge is a metric that represents a single numerical value that can arbitrarily go up and down. Usage: Helps in monitoring the fluctuation in the number of connected iOS devices over time. network_receive_bytes Type: CounterVec Description: A counter metric that represents the cumulative bytes received on the virtual TAP device. It is labeled by device and serial to provide granular tracking across different devices. Labels: device: network interface name serial: The serial number of the iOS device. Usage: Useful for analyzing the volume of incoming network traffic on a per-device basis. network_send_bytes Type: CounterVec Description: A counter metric for tracking the total bytes sent to the virtual TAP device. Similar to network_receive_bytes, it uses device and serial labels for detailed monitoring. Labels: device: network interface name serial: The serial number of the iOS device. Usage: Allows for the assessment of outgoing network traffic per device, aiding in network performance evaluation. usb_send_bytes Type: CounterVec Description: Represents the total bytes sent through the USB endpoint to connected devices. This metric is labeled by serial to differentiate between devices. Labels: serial: The serial number of the iOS device. Usage: Facilitates tracking of data sent to devices over USB, which is crucial for understanding USB throughput and identifying potential bottlenecks. usb_receive_bytes Type: CounterVec Description: Counts the bytes received from iOS devices over the USB endpoint. It is labeled with serial, allowing for device-specific traffic analysis. Labels: serial: The serial number of the iOS device. Usage: Essential for monitoring the amount of data received from devices over USB, offering insights into USB data receipt efficiency and potential issues. --------- Co-authored-by: Daniel Paulus <daniel.paulus@mesmerhq.com>
1 parent b4506e7 commit d3366ca

File tree

7 files changed

+125
-17
lines changed

7 files changed

+125
-17
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ build:
3636

3737
# Run the Go program with sudo
3838
run: build
39-
@sudo ./$(NCM_BINARY_NAME)
39+
@sudo ./$(NCM_BINARY_NAME) --prometheusport=8080
4040

4141
# Build and run
4242
up: build run

cmd/cdc-ncm/main.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"flag"
45
ncm "go-ios-cdcncm"
56
"log/slog"
67
"os"
@@ -23,10 +24,23 @@ func checkLinux() {
2324
}
2425
}
2526

27+
// accepts one cmd line argument: --prometheusport=8080
28+
// if specified, prometheus metrics will be available at http://0.0.0.0:prometheusport/metrics
29+
// if not specified, the prometheus endpoint will not be started and not be available.
2630
func main() {
2731
checkLinux()
2832
checkUsbMux()
2933
checkRoot()
34+
// Define a string flag with a default value and a short description.
35+
// This will read the command-line argument for --prometheusport.
36+
prometheusPort := flag.Int("prometheusport", -1, "The port for Prometheus metrics")
37+
// Parse the flags from the command-line arguments.
38+
flag.Parse()
39+
if *prometheusPort != -1 {
40+
go ncm.StartPrometheus(*prometheusPort)
41+
} else {
42+
slog.Info("prometheus metrics not configured. start with '--prometheusport=8080' to expose prometheus metrics.")
43+
}
3044
c := make(chan os.Signal, 1)
3145
signal.Notify(c, os.Interrupt)
3246
err := ncm.Start(c)

ncm/go.mod

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,25 @@ module go-ios-cdcncm
33
go 1.21
44

55
require (
6+
github.com/Masterminds/semver v1.5.0
7+
github.com/google/gousb v1.1.2
8+
github.com/prometheus/client_golang v1.18.0
9+
github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091
10+
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
11+
github.com/stretchr/testify v1.8.4
12+
)
13+
14+
require (
15+
github.com/beorn7/perks v1.0.1 // indirect
16+
github.com/cespare/xxhash/v2 v2.2.0 // indirect
617
github.com/davecgh/go-spew v1.1.1 // indirect
7-
github.com/google/gousb v1.1.2 // indirect
18+
github.com/kr/text v0.2.0 // indirect
19+
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
820
github.com/pmezard/go-difflib v1.0.0 // indirect
9-
github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 // indirect
10-
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect
11-
github.com/stretchr/objx v0.5.0 // indirect
12-
github.com/stretchr/testify v1.8.4 // indirect
21+
github.com/prometheus/client_model v0.5.0 // indirect
22+
github.com/prometheus/common v0.45.0 // indirect
23+
github.com/prometheus/procfs v0.12.0 // indirect
1324
golang.org/x/sys v0.15.0 // indirect
25+
google.golang.org/protobuf v1.31.0 // indirect
1426
gopkg.in/yaml.v3 v3.0.1 // indirect
1527
)

ncm/go.sum

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,50 @@
1-
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1+
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
2+
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
3+
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
4+
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
5+
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
6+
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
7+
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
28
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
39
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10+
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
11+
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
12+
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
13+
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
414
github.com/google/gousb v1.1.2 h1:1BwarNB3inFTFhPgUEfah4hwOPuDz/49I0uX8XNginU=
515
github.com/google/gousb v1.1.2/go.mod h1:GGWUkK0gAXDzxhwrzetW592aOmkkqSGcj5KLEgmCVUg=
16+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
17+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
18+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
19+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
20+
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
21+
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
622
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
723
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
24+
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
25+
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
26+
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
27+
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
28+
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
29+
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
30+
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
31+
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
32+
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
33+
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
834
github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091 h1:1zN6ImoqhSJhN8hGXFaJlSC8msLmIbX8bFqOfWLKw0w=
935
github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091/go.mod h1:N20Z5Y8oye9a7HmytmZ+tr8Q2vlP0tAHP13kTHzwvQY=
1036
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
1137
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
12-
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
13-
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
14-
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
15-
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
16-
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
17-
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
1838
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
1939
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
2040
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
2141
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
42+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
43+
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
44+
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
45+
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
2246
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
23-
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
47+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
48+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
2449
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
2550
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

ncm/metrics.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package ncm
2+
3+
import (
4+
"fmt"
5+
"log/slog"
6+
"net/http"
7+
8+
"github.com/prometheus/client_golang/prometheus"
9+
"github.com/prometheus/client_golang/prometheus/promhttp"
10+
)
11+
12+
var (
13+
deviceCount = prometheus.NewGauge(prometheus.GaugeOpts{
14+
Name: "device_count",
15+
Help: "How many iOS devices are connected",
16+
})
17+
networkReceiveBytes = prometheus.NewCounterVec(prometheus.CounterOpts{
18+
Name: "network_receive_bytes",
19+
Help: "Counter metric for received bytes on the virtual TAP device",
20+
}, []string{"device", "serial"})
21+
networkSendBytes = prometheus.NewCounterVec(prometheus.CounterOpts{
22+
Name: "network_send_bytes",
23+
Help: "Counter metric for bytes sent to the virtual TAP device",
24+
}, []string{"device", "serial"})
25+
usbSendBytes = prometheus.NewCounterVec(prometheus.CounterOpts{
26+
Name: "usb_send_bytes",
27+
Help: "Counter metric for send bytes on the usb endpoint",
28+
}, []string{"serial"})
29+
usbReceiveBytes = prometheus.NewCounterVec(prometheus.CounterOpts{
30+
Name: "usb_receive_bytes",
31+
Help: "Counter metric for received bytes on the USB endpoint",
32+
}, []string{"serial"})
33+
)
34+
35+
func StartPrometheus(port int) {
36+
prometheus.MustRegister(deviceCount)
37+
prometheus.MustRegister(networkReceiveBytes)
38+
prometheus.MustRegister(networkSendBytes)
39+
prometheus.MustRegister(usbSendBytes)
40+
prometheus.MustRegister(usbReceiveBytes)
41+
42+
// Expose metrics endpoint
43+
http.Handle("/metrics", promhttp.Handler())
44+
45+
// Start HTTP server
46+
slog.Info("prometheus metrics up", "port", port, "endpoint", "/metrics")
47+
err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
48+
if err != nil {
49+
slog.Error("failed starting metrics endpoint", "err", err)
50+
}
51+
}

ncm/ncm.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,18 @@ type NcmWrapper struct {
5858
targetWriter io.Writer
5959
buf *bytes.Buffer
6060
sequenceNum uint16
61+
serial string
6162
}
6263

6364
const headerSignature = 0x484D434E
6465

65-
func NewWrapper(targetReader io.Reader, targetWriter io.Writer) *NcmWrapper {
66+
func NewWrapper(targetReader io.Reader, targetWriter io.Writer, serial string) *NcmWrapper {
6667
return &NcmWrapper{
6768
targetReader: targetReader,
6869
targetWriter: targetWriter,
6970
buf: bytes.NewBuffer(nil),
7071
sequenceNum: 0,
72+
serial: serial,
7173
}
7274
}
7375

@@ -136,7 +138,7 @@ func (r *NcmWrapper) ReadDatagrams() ([]ethernet.Frame, error) {
136138
if err != nil {
137139
return result, fmt.Errorf("ReadDatagrams: reading block failed bytes read:%d err: %w", b, err)
138140
}
139-
141+
usbReceiveBytes.WithLabelValues(r.serial).Add(float64(h.BlockLen))
140142
offset := h.NdpIndex
141143
var dh datagramPointerHeader
142144
err = binary.Read(bytes.NewReader(ncmTransferBlock[offset:]), binary.LittleEndian, &dh)
@@ -221,6 +223,7 @@ func (r *NcmWrapper) Write(p []byte) (n int, err error) {
221223
}
222224
buf.Write(p)
223225
block = buf.Bytes()
226+
usbSendBytes.WithLabelValues(r.serial).Add(float64(len(block)))
224227
n, err = r.targetWriter.Write(block)
225228
if err != nil {
226229
return n, fmt.Errorf("write: writing ncm packet to usb failed %w", err)

ncm/usb.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func printStatus() {
4949
connectedDevices = append(connectedDevices, key.(string))
5050
return true
5151
})
52+
deviceCount.Set(float64(len(connectedDevices)))
5253
slices.Sort[[]string](connectedDevices)
5354
slog.Debug("connected devices", "devices", connectedDevices)
5455
}
@@ -268,7 +269,7 @@ func createConfig(serial string) (*water.Interface, error) {
268269
// for some reason. This happens if the device is disconnected or if the virtual network interface is removed.
269270
// Closing interfaces is the responsibility of the caller.
270271
func ncmIOCopy(w io.Writer, r io.Reader, ifce *water.Interface, serial string) error {
271-
wr := NewWrapper(r, w)
272+
wr := NewWrapper(r, w, serial)
272273
ctx, cancel := context.WithCancel(context.Background())
273274
wg := sync.WaitGroup{}
274275
wg.Add(2)
@@ -288,6 +289,7 @@ func ncmIOCopy(w io.Writer, r io.Reader, ifce *water.Interface, serial string) e
288289
cancel()
289290
continue
290291
}
292+
networkReceiveBytes.WithLabelValues(ifce.Name(), serial).Add(float64(n))
291293
frame = frame[:n]
292294
_, err = wr.Write(frame)
293295
if err != nil {
@@ -318,6 +320,7 @@ func ncmIOCopy(w io.Writer, r io.Reader, ifce *water.Interface, serial string) e
318320
slog.Error("failed sending frame to virtual device", "err", err)
319321
cancel()
320322
}
323+
networkSendBytes.WithLabelValues(ifce.Name(), serial).Add(float64(len(frame)))
321324
}
322325
}
323326
}

0 commit comments

Comments
 (0)