Suvayu Ali fatkasuvayu+linux@gmail.com writes:
On Mon, Jul 22, 2013 at 12:33:53PM +0200, lee wrote:
I've done that yesterday. It's working already, showing an automatically scaling graph of the used network bandwidth over 360 samples, with the update interval given on the command line. Let it update every 10 seconds, and you see the last hour. Xosview can't do that, and conky probably can't, either.
It also has almost zero CPU usage, and memory usage is only 40064/2272 virt/res. Now I could add showing processes or whatever I like to see :)
If you do not mind, it be educational for me to see your source.
:)
Sure --- you'll need libsx to compile it, though. Since the Fedora package providing libsx unfortunately is orphaned, I took the Debian source package, applied their patches as much as possible and managed to compile libsx. I can tar up what I have and send it to you if you want, just let me know.
I added load average monitoring today. The code is somewhat ugly because it's a derivative of a monitor I wrote to use with i3, and that one doesn't have any graphics.
Ah, two keys are currently defined:
q: quit t: set update interval
If you know a faster way to shift arrays, please let me know. I tried memmove() and it segfaulted, and I haven't looked into it further yet.
The text display can be messed up for you, depending on what fonts it will use. With libsx, you can only get the width of a text to be printed with a particular font but not the height, so for now I simply hardcoded the positions so that it fits the font it's using here.
// bwstats // This software is licensed under the GPL. // Author: lee@yun.yagibdah.de, 2013-07-21 // // compile with something like: // gcc -lsx -lXpm -lXaw -lXt -lX11 -O2 -Wall -fomit-frame-pointer // -fexpensive-optimizations -march=native bwstat.c -o bwstat // // That should compile without warnings. Now run: // // bwstat /sys/class/net/em1/statistics/rx_bytes // /sys/class/net/em1/statistics/tx_bytes // // Replace these files with whatever files have the statistics // of the network interface you want to monitor. //
#include <stdio.h> #include <sys/time.h> #include <time.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <sys/sysinfo.h>
#include <libsx.h>
#define BUFFSIZE 128 #define KIB (1024)
#define SAMPLES 360 #define TEXT 64
typedef struct { unsigned long rx; unsigned long tx; struct timeval tv; } rxtx;
typedef struct { FILE *rx; FILE *tx; char buffer[BUFFSIZE]; } filestuff;
typedef struct { double rxbw; double txbw; time_t timestamp; } bandwidth;
typedef struct { Widget w; int width; int height; float valuesTX[SAMPLES]; float valuesRX[SAMPLES]; float loadavgs[SAMPLES]; int index; } DrawAreaData;
typedef struct { Widget w; int width; int height; } DrawAreaText;
typedef struct { int txbar; int rxbar; int sepline; int loadavg; int bg; } colours;
typedef struct { filestuff *t; rxtx *new; rxtx *old; bandwidth *bw; int notimeout; unsigned long tick; DrawAreaData Data; DrawAreaText Text; colours col; } mydata;
typedef struct { float maxTX; float maxRX; float maxAV; } maxtxrx;
typedef struct { int days; int hours; int minutes; int seconds; } updays;
void sec2days(long seconds, updays *ud) { ud->days = seconds / 86400; seconds -= 86400 * ud->days; ud->hours = seconds / 3600; seconds -= 3600 * ud->hours; ud->minutes = seconds / 60; ud->seconds = seconds - 60 * ud->minutes; }
int getstat(filestuff *f, rxtx *s) { int ret = 0;
if(gettimeofday( &(s->tv), NULL) ) { perror("gettimeofday"); ret = 3; } else { bzero(f->buffer, BUFFSIZE); int c; unsigned long offset = 0; while( ( ( c = fgetc(f->rx) ) != EOF) && offset < BUFFSIZE) { *(f->buffer + offset) = c; offset++; } if(offset < BUFFSIZE) { s->rx = atol(f->buffer);
bzero(f->buffer, BUFFSIZE); offset = 0; while( ( ( c = fgetc(f->tx) ) != EOF) && offset < BUFFSIZE) { *(f->buffer + offset) = c; offset++; } if(offset < BUFFSIZE) { s->tx = atol(f->buffer); } else { ret = 2; } } else { ret = 1; } }
return ret; }
void delta(rxtx *old, rxtx *new, bandwidth *bw) { unsigned long drx = new->rx - old->rx; unsigned long dtx = new->tx - old->tx;
double dtime = (double)(new->tv.tv_sec - old->tv.tv_sec) + (double)( (double)(new->tv.tv_usec - old->tv.tv_usec) / (double)1000000.0);
bw->rxbw = (double)drx / dtime; bw->txbw = (double)dtx / dtime; bw->timestamp = new->tv.tv_sec; }
void array_shift_left(float arrayRX[], float arrayTX[], float arrayAV[], int idx) { int n = 1; while(n <= idx) { arrayRX[n] = arrayRX[n + 1]; arrayTX[n] = arrayTX[n + 1]; arrayAV[n] = arrayAV[n + 1]; n++; } }
void get_data(mydata *d) { rewind(d->t->rx); rewind(d->t->tx);
if( !getstat(d->t, d->new) ) { delta(d->old, d->new, d->bw); memcpy(d->old, d->new, sizeof(rxtx) );
double loads[1]; if(getloadavg(loads, 1) != 1) { loads[0] = 0.0; puts("error: getloadavg() failed"); }
if(d->Data.index < SAMPLES - 1) { d->Data.valuesRX[d->Data.index] = (float)(d->bw->rxbw / (double)KIB); d->Data.valuesTX[d->Data.index] = (float)(d->bw->txbw / (double)KIB); d->Data.loadavgs[d->Data.index] = (float)loads[0]; (d->Data.index)++; } else { array_shift_left(d->Data.valuesRX, d->Data.valuesTX, d->Data.loadavgs, d->Data.index); d->Data.valuesRX[d->Data.index] = (float)(d->bw->rxbw / (double)KIB); d->Data.valuesTX[d->Data.index] = (float)(d->bw->txbw / (double)KIB); d->Data.loadavgs[d->Data.index] = (float)loads[0]; } } else { puts("error: getstat() failed"); } }
void get_maxval(float vTX[], float vRX[], float vAV[], maxtxrx *m, int idx) { m->maxTX = m->maxRX = m->maxAV = 0.0; int n = 0; while(n <= idx) { if(m->maxTX < vTX[n] ) { m->maxTX = vTX[n]; } if(m->maxRX < vRX[n] ) { m->maxRX = vRX[n]; } if(m->maxAV < vAV[n] ) { m->maxAV = vAV[n]; } n++; } }
float comp_percentage(float ref, float this) { return ( (ref == 0.0) ? 0 : (int)(this * 100.0 / ref + 0.5) ); }
int comp_percent(float from, float percentage) { return (int)(percentage * from / 100.0 + 0.5); }
void draw_bar(void *data) { mydata *d = data;
SetDrawArea(d->Data.w); SetColor(d->col.sepline); SetLineWidth(2); ClearDrawArea(); int height = d->Data.height; --height; int halfheight = height / 2; DrawLine(0, halfheight, d->Data.width, halfheight); SetLineWidth(d->Data.width / SAMPLES);
int idx = d->Data.index; maxtxrx m; get_maxval(d->Data.valuesTX, d->Data.valuesRX, d->Data.loadavgs, &m, idx);
XPoint pline[SAMPLES]; int n = 0; while(n <= idx) { float percentage = comp_percentage(m.maxTX, d->Data.valuesTX[n] ); int h = comp_percent( (float)halfheight, percentage); SetColor(d->col.txbar); DrawLine(n, halfheight - 1, n, halfheight - h);
percentage = comp_percentage(m.maxRX, d->Data.valuesRX[n] ); h = comp_percent( (float)halfheight, percentage); SetColor(d->col.rxbar); DrawLine(n, halfheight + 1, n, halfheight + h);
percentage = comp_percentage(m.maxAV, d->Data.loadavgs[n] ); h = comp_percent( (float)height, percentage); pline[n].x = n; pline[n].y = height - h;
n++; } SetColor(d->col.loadavg); DrawPolyline(pline, idx);
// SyncDisplay(); SetDrawArea(d->Text.w); ClearDrawArea(); idx -= idx < SAMPLES - 1;
double loads[3]; if(getloadavg(loads, 3) == 3) { char txt[TEXT * 2]; snprintf(txt, TEXT * 2 - 1, "rx: %8.2f (%8.2f) tx: %8.2f (%8.2f)", d->Data.valuesRX[idx], m.maxRX, d->Data.valuesTX[idx], m.maxTX); SetColor(d->col.txbar); DrawText(txt, 8, 14);
struct sysinfo syi; updays up; if( !sysinfo( &syi) ) { sec2days(syi.uptime, &up); } else { memset( &up, 0, sizeof(updays) ); }
snprintf(txt, TEXT * 2 - 1, "%5.2f %5.2f %5.2f (%5.2f), up: %d:%02d:%02d:%02d", d->Data.loadavgs[idx], loads[1], loads[2], m.maxAV, up.days, up.hours, up.minutes, up.seconds); SetColor(d->col.loadavg); DrawText(txt, 12, 28); } else { char txt[TEXT * 2]; snprintf(txt, TEXT * 2 - 1, "rx: %8.2f (%8.2f) tx: %8.2f (%8.2f), %5.2f (%5.2f)", d->Data.valuesRX[idx], m.maxRX, d->Data.valuesTX[idx], m.maxTX, d->Data.loadavgs[idx], m.maxAV); DrawText(txt, 10, d->Text.height - 10); puts("error: getloadavg() failed"); } // SyncDisplay(); }
void update_display(void *data) { mydata *d = data;
if( !(d->notimeout) ) { get_data(d); }
draw_bar(d);
if( !(d->notimeout) ) { AddTimeOut(d->tick, update_display, data); } else { d->notimeout = 0; } }
void cleanup(void *data) { mydata *d = data; if(d->t->rx != NULL) { fclose(d->t->rx); } if(d->t->tx != NULL) { fclose(d->t->tx); } }
void keypress(Widget w, char *input, int up_or_down, void *data) { if( *input == 'q') { mydata *d = data; cleanup(d); exit(0); }
if( ( *input == 't') && up_or_down) { char txt[TEXT]; mydata *d = data; snprintf(txt, TEXT - 1, "%f", (float)(d->tick / 1000.0) ); char *enter = GetText("Update Interval", txt, 128, 50); if(enter) { d->tick = (unsigned long)(atof(enter) * 1000.0); if(d->tick <= 0) { d->tick = 1000; } } } }
void cb_drawarea(Widget w, int width, int height, void *data) { mydata *d = data; if(w == d->Data.w) { d->Data.width = width; d->Data.height = height; } if(w == d->Text.w) { d->Text.width = width; d->Text.height = height; }
d->notimeout = 1; update_display(data); }
int main(int argc, char *argv[] ) {
if( !OpenDisplay(argc, argv) ) { puts("cannot open display"); exit(1); }
if(argc != 3) { puts("usage: bwstat interval /path/to/statsfile-rx /path/to/statsfile-tx"); exit(1); }
filestuff t; t.rx = fopen(argv[1], "r"); if(t.rx == NULL) { perror(argv[1] ); exit(2); } t.tx = fopen(argv[2], "r"); if(t.tx == NULL) { perror(argv[2] ); fclose(t.rx); exit(3); }
bandwidth bw; memset( &bw, 0, sizeof(bandwidth) ); rxtx new; memset( &new, 0, sizeof(rxtx) ); rxtx old; memset( &old, 0, sizeof(rxtx) );
mydata display; memset( &display, 0, sizeof(mydata) ); display.t = &t; display.new = &new; display.old = &old; display.bw = &bw; display.Data.width = 360; display.Data.height = 201; display.Text.width = display.Data.width; display.Text.height = 32; display.notimeout = 0; display.tick = 5000; display.Data.w = MakeDrawArea(display.Data.width, display.Data.height, cb_drawarea, &display); display.Text.w = MakeDrawArea(display.Text.width, display.Text.height, cb_drawarea, &display);
SetWidgetPos(display.Text.w, PLACE_UNDER, display.Data.w, NO_CARE, NULL); SetKeypressCB(display.Data.w, keypress); SetKeypressCB(display.Text.w, keypress);
ShowDisplay();
display.col.rxbar = GetNamedColor("green"); display.col.txbar = GetNamedColor("darkgreen"); display.col.sepline = GetNamedColor("darkgoldenrod"); display.col.loadavg = GetNamedColor("lightslategray"); display.col.bg = GetNamedColor("gray6");
SetBgColor(display.Data.w, display.col.bg); SetBgColor(display.Text.w, display.col.bg);
update_display( (void *)( &display) ); MainLoop();
exit(0); }