├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── debian ├── changelog ├── control ├── copyright ├── rules └── source │ └── format ├── ifgraph.service ├── images └── screenshot0.png └── src ├── grapher.c ├── grapher.h ├── hsv.h ├── ifgraph.c ├── ifgraph.h └── ifgraphd.c /.gitignore: -------------------------------------------------------------------------------- 1 | ifgraphd 2 | ifgraph 3 | *.d 4 | *.o 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Bram Stolk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -g -O -Wall -Wextra -MMD -MP 2 | 3 | DISTFILES =\ 4 | LICENSE \ 5 | README.md \ 6 | Makefile \ 7 | src/ifgraph.h \ 8 | src/ifgraphd.c \ 9 | src/ifgraph.c \ 10 | src/hsv.h \ 11 | src/grapher.c \ 12 | src/grapher.h \ 13 | images \ 14 | ifgraph.service \ 15 | 16 | # Two binaries: one for back-end recording, and one for front-end displaying. 17 | all: ifgraphd ifgraph 18 | 19 | # Back-end binary 20 | ifgraphd: src/ifgraphd.o 21 | $(CC) -o $@ $^ -lrt 22 | 23 | # Front-end binary 24 | ifgraph: src/ifgraph.o src/grapher.o 25 | $(CC) -o $@ $^ -lrt -lm 26 | 27 | clean: 28 | rm -f src/*.d src/*.o ifgraphd ifgraph 29 | 30 | dist-clean: clean 31 | rm -f src/*.d 32 | 33 | install: ifgraph ifgraphd 34 | install -d ${DESTDIR}/usr/bin 35 | install -m 755 ifgraph ${DESTDIR}/usr/bin/ 36 | install -m 755 ifgraphd ${DESTDIR}/usr/bin/ 37 | install -d ${DESTDIR}/lib/systemd/system 38 | install -m 644 ifgraph.service ${DESTDIR}/lib/systemd/system/ 39 | 40 | uninstall: 41 | rm -f ${DESTDIR}/usr/bin/ifgraph 42 | rm -f ${DESTDIR}/usr/bin/ifgraphd 43 | 44 | # debuild needs an original tar ball to work off. 45 | tarball: 46 | tar cvzf ../ifgraph_1.1.orig.tar.gz $(DISTFILES) 47 | 48 | # upload the source package to the author's personal package archive. 49 | packageupload: 50 | debuild -S 51 | dput ppa:b-stolk/ppa ../ifgraph_1.1-1_source.changes 52 | 53 | # automatically generated dependencies. 54 | -include $(src/ifgraphd.d src/ifgraph.d src/grapher.d) 55 | 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ifgraph 2 | Network Interface Grapher. 3 | 4 | ![screenshot](images/screenshot0.png "screenshot") 5 | 6 | # Description 7 | 8 | The ifgraph tool will show a running graph of the bandwidth use of your network interfaces. 9 | 10 | On top, it will show a live-view of current bandwidth use, and ticks one sample per second. 11 | 12 | On the bottom, a long-term view of bandwidth use is presented, where you can cycle between a tick per minute, per hour or per day. 13 | 14 | # Unique Features 15 | 16 | The stand out feature of this tool is the zero load it places on your host. 17 | I've measured the CPU usage to be 0.0% on my machine. 18 | 19 | This is because ifgraph uses a simple in-memory ring-buffer to store the data. 20 | This ring-buffer is stored in shared memory, so not a single disk-access will be required. It really is the lightest of tools. 21 | 22 | The ifgraph tool has zero dependencies, and is two small programs written in C. 23 | 24 | The ifgraph tool will show RX/TX bandwidth, aggregated over multiple network devices. 25 | This enables you to quickly see what the traffic volume is on your WAN, your LAN, your WIFI, etc. 26 | 27 | # Building 28 | 29 | This tool has zero dependencies. 30 | Just type `make` on the command line. 31 | 32 | # Usage 33 | 34 | The tool comes in two parts, each of which is a simple C program: 35 | 36 | ifgraphd: back-end 37 | 38 | ifgraph: front-end 39 | 40 | The ifgraphd back-end process does the recording and runs as a daemon, in the background. 41 | 42 | The front-end process plots the graph in a terminal window that supports 24b colour, like a gnome terminal, xterminal, or anything similar. The front-end you can close and open at any time, and runs in the foreground. 43 | 44 | The back-end needs to be started first, and takes optional command line parameters. 45 | By default, it will record statistics of all your network devices. 46 | But if you want to limit the set, you can provide the network devices on the command line, like so: 47 | 48 | ``` 49 | ./ifgraphd eno1 eno2 & 50 | ``` 51 | 52 | To view the data that has been recorded, you launch the front end in a terminal window: 53 | 54 | ``` 55 | ./ifgraph 56 | ``` 57 | 58 | As with the back-end, the front-end also takes optional arguments: the interface names you want to graph. 59 | This set of names needs to be a subset of the names that you are recording with the back-end. 60 | 61 | # Installation 62 | 63 | For Debian and Ubuntu you can use the `make install` target to create a systemd service. 64 | 65 | To start the service: 66 | 67 | ``` 68 | $ systemctl start ifgraph 69 | $ systemctl status ifgraph 70 | ``` 71 | 72 | # Author 73 | 74 | The ifgraph tool is (c)2023 by Bram Stolk 75 | 76 | # License 77 | 78 | MIT 79 | 80 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | ifgraph (1.1-1) plucky; urgency=medium 2 | 3 | * Remove libm dependency. 4 | 5 | -- Bram Stolk Sun, 25 May 2025 14:14:44 -0700 6 | 7 | ifgraph (1.0-1) kinetic; urgency=medium 8 | 9 | * Initial release. 10 | 11 | -- Bram Stolk Mon, 16 Jan 2023 20:29:16 -0800 12 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: ifgraph 2 | Section: contrib/utils 3 | Priority: optional 4 | Maintainer: Bram Stolk 5 | Rules-Requires-Root: no 6 | Build-Depends: 7 | debhelper-compat (= 13) 8 | Standards-Version: 4.6.1 9 | Homepage: https://github.com/stolk/ifgraph 10 | 11 | Package: ifgraph 12 | Architecture: amd64 13 | Depends: ${shlibs:Depends}, ${misc:Depends} 14 | Description: Records and graphs the bandwidth use of network interfaces using the /sys filesystem. 15 | Uses a back-end daemon to record, and a front-end visualization using a text terminal that supports 24bit color. 16 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: energygraph 3 | Upstream-Contact: Bram Stolk 4 | Source: https://github.com/stolk/energygraph 5 | 6 | Files: * 7 | Copyright: 2022 Bram Stolk 8 | License: MIT 9 | 10 | License: MIT 11 | MIT License 12 | . 13 | Copyright (c) 2022 Bram Stolk 14 | . 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | . 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | . 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | # Output every command that modifies files on the build system. 4 | export DH_VERBOSE = 1 5 | 6 | 7 | # See FEATURE AREAS in dpkg-buildflags(1). 8 | #export DEB_BUILD_MAINT_OPTIONS = hardening=+all 9 | 10 | # See ENVIRONMENT in dpkg-buildflags(1). 11 | # Package maintainers to append CFLAGS. 12 | #export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic 13 | # Package maintainers to append LDFLAGS. 14 | #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 15 | 16 | 17 | %: 18 | dh $@ 19 | 20 | 21 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /ifgraph.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=igraph service 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | User=daemon 8 | ExecStart=/usr/bin/ifgraphd 9 | KillMode=control-group 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | 14 | -------------------------------------------------------------------------------- /images/screenshot0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stolk/ifgraph/c8ec77496cc209dca1dbb1a8315d14e21836c1f6/images/screenshot0.png -------------------------------------------------------------------------------- /src/grapher.c: -------------------------------------------------------------------------------- 1 | // grapher.c 2 | // 3 | // by Abraham Stolk. 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "grapher.h" 19 | 20 | 21 | #define HALFBLOCK "▀" // Uses Unicode char U+2580 22 | 23 | static int termw = 0, termh = 0; 24 | 25 | int imw = 0; 26 | int imh = 0; 27 | uint32_t* im = 0; 28 | char* overlay = 0; 29 | 30 | char postscript[400]; 31 | 32 | int grapher_resized = 1; 33 | 34 | 35 | static void get_terminal_size(void) 36 | { 37 | struct winsize tmp; 38 | ioctl(STDOUT_FILENO, TIOCGWINSZ, &tmp); 39 | termw = tmp.ws_col; 40 | termh = tmp.ws_row; 41 | } 42 | 43 | 44 | static void setup_image(void) 45 | { 46 | if (im) free(im); 47 | if (overlay) free(overlay); 48 | 49 | imw = termw; 50 | imh = 2 * (termh-1); 51 | const size_t sz = imw * imh * 4; 52 | im = (uint32_t*) malloc(sz); 53 | memset( im, 0x00, sz ); 54 | 55 | overlay = (char*) malloc( imw * (imh/2) ); 56 | memset( overlay, 0x00, imw * (imh/2) ); 57 | } 58 | 59 | 60 | static void sigwinchHandler(int sig) 61 | { 62 | (void)sig; // unused 63 | grapher_resized = 1; 64 | } 65 | 66 | 67 | static void print_image_double_res( int w, int h, unsigned char* data, char* overlay ) 68 | { 69 | if ( h & 1 ) 70 | h--; 71 | const int linesz = 32768; 72 | char line[ linesz ]; 73 | 74 | for ( int y = 0; y /dev/null 2> /dev/null") ) 118 | return -1; 119 | 120 | // Listen to changes in terminal size 121 | struct sigaction sa; 122 | sigemptyset( &sa.sa_mask ); 123 | sa.sa_flags = 0; 124 | sa.sa_handler = sigwinchHandler; 125 | if ( sigaction( SIGWINCH, &sa, 0 ) == -1 ) 126 | perror( "sigaction" ); 127 | 128 | return 0; 129 | } 130 | 131 | 132 | void grapher_adapt_to_new_size(void) 133 | { 134 | printf(CLEARSCREEN); 135 | get_terminal_size(); 136 | setup_image(); 137 | grapher_resized = 0; 138 | } 139 | 140 | 141 | void grapher_update( void ) 142 | { 143 | printf( CURSORHOME ); 144 | print_image_double_res( imw, imh, (unsigned char*) im, overlay ); 145 | 146 | printf(SETBG "0;0;0m"); 147 | 148 | printf( "%s", postscript ); 149 | fflush( stdout ); 150 | } 151 | 152 | 153 | void grapher_exit(void) 154 | { 155 | free(im); 156 | printf( RESETALL ); 157 | printf( CLEARSCREEN ); 158 | } 159 | 160 | 161 | -------------------------------------------------------------------------------- /src/grapher.h: -------------------------------------------------------------------------------- 1 | // grapher.h 2 | // 3 | // by Abraham Stolk. 4 | 5 | extern int imw; 6 | extern int imh; 7 | extern uint32_t* im; 8 | extern char* overlay; 9 | 10 | extern char postscript[400]; 11 | 12 | extern int grapher_resized; 13 | 14 | 15 | extern int grapher_init( void ); 16 | 17 | extern void grapher_adapt_to_new_size( void ); 18 | 19 | extern void grapher_update( void ); 20 | 21 | extern void grapher_exit( void ); 22 | 23 | 24 | #define RESETALL "\x1b[0m" 25 | 26 | #define CURSORHOME "\x1b[H" 27 | 28 | #define CLEARSCREEN "\e[H\e[2J\e[3J" 29 | 30 | #define SETFG "\x1b[38;2;" 31 | 32 | #define SETBG "\x1b[48;2;" 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/hsv.h: -------------------------------------------------------------------------------- 1 | // hsv.h by Bram Stolk 2 | 3 | #ifndef HSV_H 4 | #define HSV_H 5 | 6 | static __inline__ void hsv_to_rgb 7 | ( 8 | float h, float s, float v, 9 | float *r, float *g, float *b 10 | ) 11 | { 12 | if( s == 0.0f ) 13 | { 14 | // achromatic (grey) 15 | *r = *g = *b = v; 16 | return; 17 | } 18 | h *= 6.0f; // sector 0 to 5 19 | int i = (int) h; 20 | float f = h - i; // factorial part of h 21 | float p = v * ( 1.0f - s ); 22 | float q = v * ( 1.0f - s * f ); 23 | float t = v * ( 1.0f - s * ( 1.0f - f ) ); 24 | switch( i ) 25 | { 26 | case 0: 27 | *r = v; 28 | *g = t; 29 | *b = p; 30 | break; 31 | case 1: 32 | *r = q; 33 | *g = v; 34 | *b = p; 35 | break; 36 | case 2: 37 | *r = p; 38 | *g = v; 39 | *b = t; 40 | break; 41 | case 3: 42 | *r = p; 43 | *g = q; 44 | *b = v; 45 | break; 46 | case 4: 47 | *r = t; 48 | *g = p; 49 | *b = v; 50 | break; 51 | default: // case 5: 52 | *r = v; 53 | *g = p; 54 | *b = q; 55 | break; 56 | } 57 | } 58 | 59 | 60 | static __inline__ uint32_t hsv_to_rgb24(float h, float s, float v) 61 | { 62 | float red,grn,blu; 63 | hsv_to_rgb(h,s,v, &red,&grn,&blu); 64 | const uint8_t r = (uint8_t)( red * 255.99f); 65 | const uint8_t g = (uint8_t)( grn * 255.99f); 66 | const uint8_t b = (uint8_t)( blu * 255.99f); 67 | return (r<<16) | (g<<8) | (b<<0); 68 | } 69 | 70 | 71 | static __inline__ float hue_to_rgb 72 | ( 73 | float p, 74 | float q, 75 | float t 76 | ) 77 | { 78 | if ( t < 0 ) t += 1; 79 | if ( t > 1 ) t -= 1; 80 | if ( t < 1/6.0f ) return p + (q - p) * 6.0f * t; 81 | if ( t < 1/2.0f ) return q; 82 | if ( t < 2/3.0f ) return p + (q - p) * (2.0f/3.0f - t) * 6.0f; 83 | return p; 84 | } 85 | 86 | 87 | static __inline__ void hsl_to_rgb 88 | ( 89 | float h, float s, float l, 90 | float* r, float* g, float* b 91 | ) 92 | { 93 | if ( s == 0 ) 94 | { 95 | *r = *g = *b = l; // achromatic 96 | } 97 | else 98 | { 99 | float q = l < 0.5f ? l * ( 1 + s ) : l + s - l * s; 100 | float p = 2 * l - q; 101 | *r = hue_to_rgb( p, q, h + 1/3.0f ); 102 | *g = hue_to_rgb( p, q, h ); 103 | *b = hue_to_rgb( p, q, h - 1/3.0f ); 104 | } 105 | } 106 | 107 | 108 | static __inline__ float linear_to_srgb(float val) 109 | { 110 | if (val <= 0.0031308f) 111 | return 12.92f * val; 112 | else 113 | return 1.055f * powf(val, 1.0f / 2.4f) - 0.055f; 114 | } 115 | 116 | 117 | static __inline__ float srgb_to_linear(float val) 118 | { 119 | if (val < 0.04045f) 120 | return val * (1.0f / 12.92f); 121 | else 122 | return powf((val + 0.055f) * (1.0f / 1.055f), 2.4f); 123 | } 124 | 125 | 126 | #endif 127 | 128 | -------------------------------------------------------------------------------- /src/ifgraph.c: -------------------------------------------------------------------------------- 1 | // 2 | // ifgraph.c 3 | // 4 | // ifgraph front-end. 5 | // 6 | // (c)2023 by Bram Stolk (b.stolk@gmail.com) 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "ifgraph.h" // definitions shared between front-end / back-end. 28 | #include "grapher.h" // plots an image to the terminal. 29 | #include "hsv.h" // convert between hue-saturation-value and red-grn-blu colours. 30 | 31 | 32 | // The candidates for network interfaces which we can analyze. 33 | static int numcand=0; 34 | static char candidates[MAXIF][32]; 35 | static int maskedcandidates[MAXIF]; 36 | 37 | // The actual interfaces that we analyze. 38 | static int numif=0; 39 | static char ifnames[MAXIF][32]; 40 | static int fd_shm[MAXIF]; 41 | static statistics_t* statistics[MAXIF]; 42 | 43 | static const int periods[RESCNT] = { 1, 60, 60*60, 60*60*24 }; 44 | static const char* periodnames[RESCNT] = { "secs", "mins", "hrs", "days" }; 45 | static int y_scale_indices[RESCNT]; 46 | static uint32_t colours_rx[MAXIF]; 47 | static uint32_t colours_tx[MAXIF]; 48 | 49 | static int selected_res = 1; 50 | static const uint64_t scalebasis[] = 51 | { 52 | 15UL, 53 | 20UL, 54 | 30UL, 55 | 40UL, 56 | 60UL, 57 | 80UL, 58 | 100UL, 59 | }; 60 | #define ORDERS_OF_MAGNITUDE_IN_SCALING 10 // Supports 100Gbps scale. 61 | #define BASISLEN (sizeof(scalebasis) / sizeof(uint64_t)) 62 | #define NUMAXISSCALES (ORDERS_OF_MAGNITUDE_IN_SCALING * BASISLEN) 63 | static uint64_t axisscales[NUMAXISSCALES]; 64 | 65 | 66 | // At the bottom of the screen we will place a line containing the legend for the graph. 67 | static void set_postscript(void) 68 | { 69 | postscript[0] = 0; 70 | for (int i=0; i> 0)&0xff, 83 | (colours_rx[i]>> 8)&0xff, 84 | (colours_rx[i]>>16)&0xff 85 | ); 86 | snprintf 87 | ( 88 | legend_tx, 89 | sizeof(legend_tx) - 1, 90 | SETFG "%hhu;%hhu;%hhumTX ", 91 | (colours_tx[i]>> 0)&0xff, 92 | (colours_tx[i]>> 8)&0xff, 93 | (colours_tx[i]>>16)&0xff 94 | ); 95 | strncat(postscript, legend_rx, sizeof(postscript) - 1 - strlen(postscript)); 96 | strncat(postscript, legend_tx, sizeof(postscript) - 1 - strlen(postscript)); 97 | }; 98 | } 99 | 100 | 101 | // Terminal handling 102 | static struct termios orig_termios; 103 | static void disableRawMode() 104 | { 105 | tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios); 106 | } 107 | static void enableRawMode() 108 | { 109 | tcgetattr(STDIN_FILENO, &orig_termios); 110 | atexit(disableRawMode); 111 | struct termios raw = orig_termios; 112 | raw.c_lflag &= ~(ECHO); // Don't echo key presses. 113 | raw.c_lflag &= ~(ICANON); // Read by char, not by line. 114 | raw.c_cc[VMIN] = 0; // No minimum nr of chars. 115 | raw.c_cc[VTIME] = 0; // No waiting time. 116 | tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw); 117 | } 118 | 119 | 120 | static void draw_overlay(int res, int y0, int y1) 121 | { 122 | // Clear the ticks on the overlay. 123 | for (int y=y0; y= 10000000000UL) 138 | { 139 | units = "Gbps"; 140 | bw = bw / 1000000000UL; 141 | } 142 | else if (maxbw >= 10000000UL) 143 | { 144 | units = "Mbps"; 145 | bw = bw / 1000000UL; 146 | } 147 | else if (maxbw >= 10000UL) 148 | { 149 | units = "Kbps"; 150 | bw = bw / 1000UL; 151 | } 152 | const int tloc = y0 + (i * height) / numbands; 153 | snprintf(overlay + imw * tloc + 1, 20, "%u %s", (uint32_t)bw, units); 154 | } 155 | // Draw the title. 156 | char title[80] = {0,}; 157 | snprintf 158 | ( 159 | title, 160 | sizeof(title), 161 | "last %d%s%s", 162 | imw-2, 163 | periodnames[res], 164 | res ? " [SPACE to cycle]" :"" 165 | ); 166 | int len = (int) strlen(title); 167 | strncpy(overlay + imw * (y0) + (imw-len)/2, title, 32); 168 | } 169 | 170 | 171 | static void choose_colours(void) 172 | { 173 | const float delta_hue = 1.0f / (numif+1); 174 | for (int i=0; i=0 && to>=0); 198 | assert(to>=fr); 199 | const int x = (imw-1) - 1 - histidx; 200 | const int64_t height = y1-y0; 201 | const int64_t l0 = (int64_t) (fr * 1.0f * height / maxbw + 0.5f); 202 | const int64_t l1 = (int64_t) (to * 1.0f * height / maxbw + 0.5f); 203 | const int64_t y_hi = height-l0; 204 | const int64_t y_lo = height-l1; 205 | for (int64_t y=y_lo; y=0 && y= height-1; 209 | } 210 | 211 | 212 | static int draw_samples(int res, int y0, int y1) 213 | { 214 | assert(y0 <= imh); 215 | assert(y1 <= imh); 216 | 217 | int height = y1-y0; 218 | 219 | // Clear the background with dark grey bands. 220 | const uint8_t blck = 0x12; 221 | const uint8_t grey = 0x1f; 222 | const int y_scl_idx = y_scale_indices[res]; 223 | const uint64_t maxbw = axisscales[y_scl_idx]; 224 | int numbands = maxbw % 3 == 0 ? 3 : 4; 225 | for (int y=0; ywr[res] - x) & HISTMSK; 253 | uint64_t rx = statistics[i]->rx[res][idx] * 8; 254 | uint64_t tx = statistics[i]->tx[res][idx] * 8; 255 | overflow += draw_range(x, maxval, colours_tx[i], cumul+0, cumul+tx, y0, y1); 256 | overflow += draw_range(x, maxval, colours_rx[i], cumul+tx, cumul+tx+rx, y0, y1); 257 | cumul += (rx+tx); 258 | if (cumul > maxval/2) 259 | underflow = 0; 260 | } 261 | } 262 | int rescaled = 0; 263 | if (overflow) 264 | { 265 | if (y_scale_indices[res] < (int)NUMAXISSCALES-1) 266 | { 267 | y_scale_indices[res] += 1; 268 | rescaled = 1; 269 | } 270 | } 271 | else if (underflow) 272 | { 273 | if (y_scale_indices[res] > 0) 274 | { 275 | y_scale_indices[res] -= 1; 276 | rescaled = 1; 277 | } 278 | } 279 | return rescaled; 280 | } 281 | 282 | 283 | static void prepare_drawing(void) 284 | { 285 | // Set up scaling for Y-axis, that needs to range between 15bps and 100Gbps. 286 | // We want "neat" numbers at the axis ticks. 287 | uint64_t mult = 1UL; 288 | int writer = 0; 289 | for (int o=0; o= RESCNT ? 1 : selected_res; 344 | } 345 | } 346 | 347 | usleep(rescaled ? 50000 : 1000000); 348 | } while(!done); 349 | 350 | grapher_exit(); 351 | } 352 | 353 | 354 | static void cleanup(void) 355 | { 356 | for (int i=0; id_type & DT_LNK)) 390 | { 391 | const int is_vl = strncmp(entry->d_name, "veth", 4) == 0 ? 1 : 0; 392 | if (!is_vl) 393 | { 394 | strncpy(candidates[numcand], entry->d_name, sizeof(candidates[numcand]) - 1); 395 | numcand++; 396 | } 397 | } 398 | } while(entry && numcand < MAXIF); 399 | 400 | // The set of network interfaces may have been limited on the command line. 401 | if (argc>1) 402 | { 403 | memset(maskedcandidates, 1, sizeof(maskedcandidates)); 404 | for (int i=1; i 0) 423 | { 424 | fd_shm[numif] = fd; 425 | strncpy(ifnames[numif], name, sizeof(ifnames[numif]) - 1); 426 | const size_t sz = sizeof(statistics_t); 427 | statistics[numif] = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0); 428 | if (statistics[numif] == MAP_FAILED) 429 | fprintf(stderr, "mmap failed: %s\n", strerror(errno)); 430 | else 431 | numif += 1; 432 | } 433 | } 434 | 435 | if (!numif) 436 | { 437 | fprintf(stderr, "None of the %d candidate network interfaces had shared memory blocks defined for it.\n", numcand); 438 | fprintf(stderr, "Is the ifgraphd daemon running?\n"); 439 | exit(3); 440 | } 441 | 442 | fprintf(stderr, "Reading statistics for "); 443 | for (int i=0; i 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "ifgraph.h" 24 | 25 | 26 | // The candidates for network interfaces which we can analyze. 27 | static int numcand=0; 28 | static char candidates[MAXIF][32]; 29 | 30 | // The actual interfaces that we analyze. 31 | static int numif=0; 32 | static char ifnames[MAXIF][32]; 33 | static FILE* f_rx[MAXIF]; 34 | static FILE* f_tx[MAXIF]; 35 | static int fd_shm[MAXIF]; 36 | static statistics_t* statistics[MAXIF]; 37 | 38 | // Last read values, from which we compute deltas. 39 | static uint64_t previous_rx[MAXIF][RESCNT]; 40 | static uint64_t previous_tx[MAXIF][RESCNT]; 41 | 42 | // The seconds-graph needs to record on every tick. 43 | // The days-graph only needs to record on every 86400 ticks. 44 | static int ticks[RESCNT]; 45 | static const int periods[RESCNT] = { 1, 60, 60*60, 60*60*24 }; 46 | 47 | 48 | // Sample network interface statistics. 49 | static void sample(int ifnr, uint64_t* rx, uint64_t* tx) 50 | { 51 | char info_rx[32] = {0,}; 52 | char info_tx[32] = {0,}; 53 | const int numr_rx = fread(info_rx, 1, sizeof(info_rx)-1, f_rx[ifnr]); 54 | const int numr_tx = fread(info_tx, 1, sizeof(info_tx)-1, f_tx[ifnr]); 55 | assert(numr_rx > 0); 56 | assert(numr_tx > 0); 57 | rewind(f_rx[ifnr]); 58 | rewind(f_tx[ifnr]); 59 | *rx = atol(info_rx); 60 | *tx = atol(info_tx); 61 | } 62 | 63 | 64 | // Record all samples for all interfaces for this tick. 65 | static int record(void) 66 | { 67 | int numrecorded = 0; 68 | for (int ifnr=0; ifnrwr + res; 79 | *wr = (*wr+1) & HISTMSK; 80 | const uint64_t d_rx = rx - previous_rx[ifnr][res]; 81 | const uint64_t d_tx = tx - previous_tx[ifnr][res]; 82 | //fprintf(stderr, "res: %d d_rx: %" PRIu64 " d_tx: %" PRIu64 "\n", res, d_rx, d_tx); 83 | statistics[ifnr]->rx[res][*wr] = d_rx; 84 | statistics[ifnr]->tx[res][*wr] = d_tx; 85 | previous_rx[ifnr][res] = rx; 86 | previous_tx[ifnr][res] = tx; 87 | numrecorded += 1; 88 | } 89 | } 90 | } 91 | for (int res=0; res= periods[res]) 95 | ticks[res] = 0; 96 | } 97 | return numrecorded; 98 | } 99 | 100 | 101 | static int unlink_shared_memory_blocks(void) 102 | { 103 | int numunlinked=0; 104 | for (int i=0; id_type & DT_LNK)) 230 | { 231 | const char* name = entry->d_name; 232 | const int is_lo = strncmp(name, "lo", 2) == 0 ? 1 : 0; 233 | const int is_br = strncmp(name, "br", 2) == 0 ? 1 : 0; 234 | const int is_ma = strncmp(name, "bonding_masters", 15) == 0 ? 1: 0; 235 | const int is_vl = strncmp(name, "veth", 4) == 0 ? 1 : 0; 236 | if (!is_lo && !is_br && !is_ma && !is_vl) // Don't want loopbacks, bridges, masters or vlans. 237 | { 238 | strncpy(candidates[numcand], name, sizeof(candidates[numcand]) - 1); 239 | numcand++; 240 | } 241 | } 242 | } while(entry && numcand < MAXIF); 243 | 244 | // If specified on command line, use those interface names, else use all candidates. 245 | if (argc==1) 246 | { 247 | memcpy(ifnames, candidates, sizeof(ifnames)); 248 | numif = numcand; 249 | } 250 | else 251 | { 252 | for (int i=1; i1) 268 | { 269 | fprintf(stderr, "No interface selected. Candidates are:\n"); 270 | for (int i=0; i