==> src/net.c <==1/* crates/mesh-top/src/net.rs, ported. */
2#include "mesh/top_net.h"
3
4#include "rsfmt.h"
5
6#include <stdio.h>
7
8#include <math.h>
9#include <stdlib.h>
10#include <string.h>
11
12#define BACKWARDS ((uint64_t)1 << 62)
13
14void mt_meter_push(mt_meter *m, uint64_t t, uint64_t v) {
15 if (m->n) {
16 uint64_t lt = m->s[m->n - 1][0], lv = m->s[m->n - 1][1];
17 if (t < lt || v - lv >= BACKWARDS) m->n = 0; /* a restart or a reset: start over */
18 else if (t == lt) m->n--; /* the same instant again: keep the newer value */
19 }
20 if (m->n == m->cap) {
21 m->cap = m->cap ? m->cap * 2 : 8;
22 m->s = realloc(m->s, m->cap * sizeof *m->s);
23 if (!m->s) abort();
24 }
25 m->s[m->n][0] = t;
26 m->s[m->n][1] = v;
27 m->n++;
28 while (m->n > 2 && t - m->s[1][0] >= MT_NET_WINDOW_MS) {
29 memmove(m->s, m->s + 1, (m->n - 1) * sizeof *m->s);
30 m->n--;
31 }
32}
33
34double mt_meter_per_s(const mt_meter *m) {
35 if (m->n == 0) return 0.0;
36 uint64_t at = m->s[0][0], av = m->s[0][1], bt = m->s[m->n - 1][0], bv = m->s[m->n - 1][1];
37 if (bt <= at) return 0.0;
38 return (double)(bv - av) * 1000.0 / (double)(bt - at);
39}
40
41void mt_meter_free(mt_meter *m) {
42 free(m->s);
43 *m = (mt_meter){NULL, 0, 0};
44}
45
46void mt_link_frames_per_s(const mt_link_track *l, size_t kind, double *rx, double *tx) {
47 *rx = mt_meter_per_s(&l->rx_frames[kind]);
48 *tx = mt_meter_per_s(&l->tx_frames[kind]);
49}
50
51static void nic_push(mt_nic_track *t, uint64_t at, const uint64_t v[8]) {
52 for (int i = 0; i < 8; i++) mt_meter_push(&t->v[i], at, v[i]);
53}
54static void nic_free(mt_nic_track *t) {
55 for (int i = 0; i < 8; i++) mt_meter_free(&t->v[i]);
56}
57double mt_nic_rx(const mt_nic_track *t) { return mt_meter_per_s(&t->v[0]); }
58double mt_nic_tx(const mt_nic_track *t) { return mt_meter_per_s(&t->v[1]); }
59void mt_nic_packets(const mt_nic_track *t, double *rx, double *tx) {
60 *rx = mt_meter_per_s(&t->v[2]);
61 *tx = mt_meter_per_s(&t->v[3]);
62}
63double mt_nic_errors(const mt_nic_track *t) { return mt_meter_per_s(&t->v[4]) + mt_meter_per_s(&t->v[5]); }
64double mt_nic_drops(const mt_nic_track *t) { return mt_meter_per_s(&t->v[6]) + mt_meter_per_s(&t->v[7]); }
65
66static void link_free(mt_link_track *l) {
67 mt_meter_free(&l->rx);
68 mt_meter_free(&l->tx);
69 for (size_t i = 0; i < MESH_NET_KINDS; i++) {
70 mt_meter_free(&l->rx_frames[i]);
71 mt_meter_free(&l->tx_frames[i]);
72 }
73}
74
75void mt_net_free(mt_net_track *n) {
76 for (size_t k = 0; k < MESH_MAX_SYSTEMS; k++) link_free(&n->links[k]);
77 for (size_t i = 0; i < n->nnics; i++) nic_free(&n->nics[i].t);
78 free(n->nics);
79 nic_free(&n->total);
80 for (int i = 0; i < 3; i++) {
81 mt_meter_free(&n->ctl[i]);
82 mt_meter_free(&n->http[i]);
83 }
84 free(n->hist);
85 free(n->mesh_hist);
86 memset(n, 0, sizeof *n);
87}
88
89void mt_net_update(mt_net_track *n, const mt_snapshot *s) {
90 uint64_t t = mt_snapshot_uptime_ms(s);
91 if (t < n->uptime) mt_net_free(n); /* meshd restarted: every count starts over */
92 n->uptime = t;
93 n->has_links = true;
94 for (uint32_t k = 0; k < MESH_MAX_SYSTEMS; k++) {
95 mt_link_stat l = mt_snapshot_link(s, k);
96 mt_link_track *lt = &n->links[k];
97 mt_meter_push(<->rx, t, l.rx_bytes);
98 mt_meter_push(<->tx, t, l.tx_bytes);
99 for (size_t i = 0; i < MESH_NET_KINDS; i++) {
100 mt_meter_push(<->rx_frames[i], t, l.rx_frames[i]);
101 mt_meter_push(<->tx_frames[i], t, l.tx_frames[i]);
102 }
103 }
104 uint64_t ctl[3], http[3];
105 mt_snapshot_ports(s, ctl, http);
106 for (int i = 0; i < 3; i++) {
107 mt_meter_push(&n->ctl[i], t, ctl[i]);
108 mt_meter_push(&n->http[i], t, http[i]);
109 }
110 uint64_t at;
111 uint32_t samples;
112 mt_snapshot_net_at(s, &at, &samples);
113 if (at > 0 && at != n->nic_at) {
114 n->nic_at = at;
115 mt_nic total = mt_snapshot_net_total(s);
116 nic_push(&n->total, at, total.v);
117 mt_nic nics[MESH_MAX_NICS];
118 size_t nn = mt_snapshot_nics(s, nics);
119 /* Keep only the NICs still reported. */
120 size_t w = 0;
121 for (size_t i = 0; i < n->nnics; i++) {
122 bool keep = false;
123 for (size_t j = 0; j < nn; j++) keep |= strcmp(nics[j].name, n->nics[i].name) == 0;
124 if (keep) n->nics[w++] = n->nics[i];
125 else nic_free(&n->nics[i].t);
126 }
127 n->nnics = w;
128 for (size_t j = 0; j < nn; j++) {
129 size_t i = 0;
130 while (i < n->nnics && strcmp(n->nics[i].name, nics[j].name) < 0) i++;
131 if (i == n->nnics || strcmp(n->nics[i].name, nics[j].name) != 0) {
132 n->nics = realloc(n->nics, (n->nnics + 1) * sizeof *n->nics);
133 if (!n->nics) abort();
134 memmove(&n->nics[i + 1], &n->nics[i], (n->nnics - i) * sizeof *n->nics);
135 memset(&n->nics[i], 0, sizeof n->nics[i]);
136 strcpy(n->nics[i].name, nics[j].name);
137 n->nnics++;
138 }
139 nic_push(&n->nics[i].t, at, nics[j].v);
140 }
141 if (n->total.v[0].n >= 2) {
142 if (!n->hist) {
143 n->hist = calloc(MT_NET_HISTORY, sizeof *n->hist);
144 n->mesh_hist = calloc(MT_NET_HISTORY, sizeof *n->mesh_hist);
145 if (!n->hist || !n->mesh_hist) abort();
146 }
147 if (n->nhist == MT_NET_HISTORY) {
148 memmove(n->hist, n->hist + 1, (MT_NET_HISTORY - 1) * sizeof *n->hist);
149 memmove(n->mesh_hist, n->mesh_hist + 1, (MT_NET_HISTORY - 1) * sizeof *n->mesh_hist);
150 n->nhist--;
151 }
152 double sum = mt_nic_rx(&n->total) + mt_nic_tx(&n->total), mesh = mt_net_mesh_bytes(n);
153 n->hist[n->nhist] = sum > 0 ? (sum >= 1.8446744073709552e19 ? UINT64_MAX : (uint64_t)sum) : 0;
154 n->mesh_hist[n->nhist] = mesh > 0 ? (mesh >= 1.8446744073709552e19 ? UINT64_MAX : (uint64_t)mesh) : 0;
155 n->nhist++;
156 }
157 }
158}
159
160bool mt_net_nic_age_ms(const mt_net_track *n, uint64_t *age) {
161 if (n->nic_at == 0) return false;
162 *age = n->uptime > n->nic_at ? n->uptime - n->nic_at : 0;
163 return true;
164}
165
166const mt_link_track *mt_net_link(const mt_net_track *n, uint32_t k) {
167 if (!n->has_links || k >= MESH_MAX_SYSTEMS) return NULL;
168 return &n->links[k];
169}
170
171const mt_nic_track *mt_net_nic(const mt_net_track *n, const char *name) {
172 for (size_t i = 0; i < n->nnics; i++)
173 if (strcmp(n->nics[i].name, name) == 0) return &n->nics[i].t;
174 return NULL;
175}
176
177double mt_net_mesh_bytes(const mt_net_track *n) {
178 double s = 0;
179 if (!n->has_links) return 0;
180 for (size_t k = 0; k < MESH_MAX_SYSTEMS; k++) s += mt_meter_per_s(&n->links[k].rx) + mt_meter_per_s(&n->links[k].tx);
181 return s;
182}
183
184bool mt_net_mesh_share(const mt_net_track *n, double *share) {
185 double t = mt_nic_rx(&n->total) + mt_nic_tx(&n->total);
186 if (!(t > 0.0)) return false;
187 double x = mt_net_mesh_bytes(n) / t;
188 *share = x < 1.0 ? x : 1.0;
189 return true;
190}
191
192static double scale(double x, const char **u) {
193 x = isfinite(x) ? (x > 0.0 ? x : 0.0) : 0.0;
194 if (x >= 1e9) {
195 *u = "G";
196 return x / 1e9;
197 }
198 if (x >= 1e6) {
199 *u = "M";
200 return x / 1e6;
201 }
202 if (x >= 1e3) {
203 *u = "k";
204 return x / 1e3;
205 }
206 *u = "";
207 return x;
208}
209
210void mt_human(double bps, char *out, size_t cap) {
211 const char *u;
212 double v = scale(bps, &u);
213 char num[64];
214 rsfmt_fixed(num, sizeof num, v, *u ? 1 : 0, 0);
215 if (*u) snprintf(out, cap, "%s %sB/s", num, u);
216 else snprintf(out, cap, "%s B/s", num);
217}
218
219void mt_short(double x, char *out, size_t cap) {
220 const char *u;
221 double v = scale(x, &u);
222 char num[64];
223 rsfmt_fixed(num, sizeof num, v, !*u || v >= 100.0 ? 0 : 1, 0);
224 snprintf(out, cap, "%s%s", num, u);
225}
226
227void mt_spark(const uint64_t *v, size_t n, size_t width, char *out) {
228 static const char *const BARS[8] = {"▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"};
229 size_t skip = n > width ? n - width : 0, shown = n - skip;
230 uint64_t max = 0;
231 for (size_t i = skip; i < n; i++)
232 if (v[i] > max) max = v[i];
233 if (max < 1) max = 1;
234 size_t k = 0;
235 for (size_t i = shown; i < width; i++) out[k++] = ' ';
236 for (size_t i = skip; i < n; i++) {
237 size_t b = (size_t)round((double)v[i] / (double)max * 7.0);
238 memcpy(out + k, BARS[b], 3);
239 k += 3;
240 }
241 out[k] = 0;
242}