skip to content
==> 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(&lt->rx, t, l.rx_bytes);
98        mt_meter_push(&lt->tx, t, l.tx_bytes);
99        for (size_t i = 0; i < MESH_NET_KINDS; i++) {
100            mt_meter_push(&lt->rx_frames[i], t, l.rx_frames[i]);
101            mt_meter_push(&lt->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}