]> arthur.barton.de Git - netdata.git/commitdiff
added support for Disk I/O monitoring, added mutex locking to sync threads
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 23 Mar 2014 16:03:11 +0000 (18:03 +0200)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 23 Mar 2014 16:03:11 +0000 (18:03 +0200)
example.html
netdata.c
netdata.js
netdata.start

index 911a63b00cabc725da8e266b3bddf0919a4a236e..031c9471de24c57153102aa726f67bf305646771 100644 (file)
@@ -4,52 +4,71 @@
         * {font-family:Arial}
         div {float: left; margin: 0 0 0 0; }
 </style>
-<title>netdata</title>
+<title>box.home.tsaousis.gr netdata</title>
 <head>
        <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
 
        <!--Load the AJAX API-->
        <script type="text/javascript" src="https://www.google.com/jsapi"></script>
-       <script type="text/javascript" src="jquery-1.10.1.min.js"></script>
+       <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
        <script type="text/javascript" src="netdata.js"></script>
        <script type="text/javascript">
        
        // Set a callback to run when the Google Visualization API is loaded.
        google.setOnLoadCallback(drawCharts);
-
+       
        function drawCharts() {
-               var width = (window.innerWidth - 50) / 2;
-               var height = 260;
-               
-               if(width < height) width = height; //>
+               var width = 3; // if zero, auto-adjusts to 50% of screen, 1-10 goes 1/width of screen
+               var height = 4;
                
                // EDIT: add one line per interface you have
                // EDIT: 
-               // EDIT:   name    div id                    json data    graph title
-               // EDIT: --------------------------------------------------------------------------------
-               drawChart('eth0', 'eth0_div', width, height, "eth0.json", "Live Network Usage for eth0");
-               drawChart('tun0', 'tun0_div', width, height, "tun0.json", "Live Network Usage for tun0");
-               drawChart('ppp0', 'ppp0_div', width, height, "ppp0.json", "Live Network Usage for ppp0");
+               // EDIT:   name    div id                    json data           graph                                  vertical axis title
+               // EDIT: --------------------------------------------------------------------------------------------------
+               drawChart('disk.sde', 'disk_sde_div', width, height, "data/disk.sde/120/2/average/", "Live Disk I/O for USB HD (disk.sde)", "I/O in kilobytes/s");
+               drawChart('disk.md125', 'disk_md125_div', width, height, "data/disk.md125/120/2/average/", "Live Disk I/O for rootfs (disk.md125)", "I/O in kilobytes/s");
+               drawChart('disk.md126', 'disk_md126_div', width, height, "data/disk.md126/120/2/average/", "Live Disk I/O for swap (disk.md126)", "I/O in kilobytes/s");
+               drawChart('disk.md127', 'disk_md127_div', width, height, "data/disk.md127/120/2/average/", "Live Disk I/O for /data (disk.md127)", "I/O in kilobytes/s");
+               drawChart('net.dsl0', 'net_dsl0_div', width, height, "data/net.dsl0/120/2/average/", "Live Network Usage for ADSL (net.dsl0)", "bandwidth in kilobits/s");
+               drawChart('net.eth3', 'net_eth3_div', width, height, "data/net.eth3/120/2/average/", "Live Network Usage for AWMN (net.eth3)", "bandwidth in kilobits/s");
+               drawChart('net.eth0', 'net_eth0_div', width, height, "data/net.eth0/120/2/average/", "Live Network Usage for LAN (net.eth0)", "bandwidth in kilobits/s");
+               drawChart('net.exohiko', 'net_exohiko_div', width, height, "data/net.exohiko/120/2/average/", "Live Network Usage for Exohiko (net.exohiko)", "bandwidth in kilobits/s");
+               drawChart('net.tun2', 'net_tun2_div', width, height, "data/net.tun2/120/2/average/", "Live Network Usage for Gregory (net.tun2)", "bandwidth in kilobits/s");
+               drawChart('net.tun0', 'net_tun0_div', width, height, "data/net.tun0/120/2/average/", "Live Network Usage for Realize (net.tun0)", "bandwidth in kilobits/s");
        }
        
-        function myRefresh() {
-               // refresh up to 4 charts every time
-                refreshCharts(4);
-        }
+       var refreshCount = 0;
+       function myChartsRefresh() {
+               refreshCount++;
+               if(refreshCount > 500) location.reload();
+
+               // refresh up to 1 charts per second
+               refreshCharts(1);
+       }
        
-        // EDIT: how often the charts are updated, in milliseconds
-        setInterval(myRefresh, 1000);
+       setInterval(myChartsRefresh, 400);
+
+       window.onresize = function(event) {
+               refreshCharts(999999);
+       };
        </script>
-</head>
+
+       </head>
 
 <body>
        <!--
                EDIT: add one div per interface you have
                EDIT: use the same id above and bellow!
        -->
-
-       <div id="eth0_div"></div>
-       <div id="tun0_div"></div>
-       <div id="ppp0_div"></div>
+       <div id="net_dsl0_div"></div>
+       <div id="net_eth3_div"></div>
+       <div id="net_eth0_div"></div>
+       <div id="net_exohiko_div"></div>
+       <div id="net_tun0_div"></div>
+       <div id="net_tun2_div"></div>
+       <div id="disk_md125_div"></div>
+       <div id="disk_md126_div"></div>
+       <div id="disk_md127_div"></div>
+       <div id="disk_sde_div"></div>
  </body>
 </html>
index eb964201850073fea62f7227e368fc14950f2e6b..893b04bf192536c7d8e3e7849607ad9e217eee80 100755 (executable)
--- a/netdata.c
+++ b/netdata.c
 #define MAX_PROC_NET_DEV_LINE 4096\r
 #define MAX_PROC_NET_DEV_IFACE_NAME 1024\r
 \r
+#define MAX_PROC_DISKSTATS_LINE 4096\r
+#define MAX_PROC_DISKSTATS_DISK_NAME 1024\r
+\r
+\r
 int silent = 0;\r
 int save_history = HISTORY;\r
 int update_every = UPDATE_EVERY;\r
@@ -293,6 +297,8 @@ struct rrd_dimension {
 typedef struct rrd_dimension RRD_DIMENSION;\r
 \r
 struct rrd_stats {\r
+       pthread_mutex_t mutex;\r
+\r
        char name[RRD_STATS_NAME_MAX + 1];\r
 \r
        size_t entries;\r
@@ -305,6 +311,7 @@ struct rrd_stats {
 typedef struct rrd_stats RRD_STATS;\r
 \r
 RRD_STATS *root = NULL;\r
+pthread_mutex_t root_mutex = PTHREAD_MUTEX_INITIALIZER;\r
 \r
 RRD_STATS *rrd_stats_create(const char *name, unsigned long entries)\r
 {\r
@@ -327,11 +334,18 @@ RRD_STATS *rrd_stats_create(const char *name, unsigned long entries)
 \r
        st->entries = entries;\r
        st->last_entry = 0;\r
-\r
        st->dimensions = NULL;\r
+\r
+       pthread_mutex_init(&st->mutex, NULL);\r
+       pthread_mutex_lock(&st->mutex);\r
+       pthread_mutex_lock(&root_mutex);\r
+\r
        st->next = root;\r
        root = st;\r
 \r
+       pthread_mutex_unlock(&root_mutex);\r
+       // leave st->mutex locked\r
+\r
        return(st);\r
 }\r
 \r
@@ -366,12 +380,15 @@ RRD_DIMENSION *rrd_stats_dimension_add(RRD_STATS *st, const char *name, size_t b
 \r
 RRD_STATS *rrd_stats_find(const char *name)\r
 {\r
+       pthread_mutex_lock(&root_mutex);\r
+\r
        RRD_STATS *st = root;\r
 \r
        for ( ; st ; st = st->next ) {\r
                if(strcmp(st->name, name) == 0) break;\r
        }\r
 \r
+       pthread_mutex_unlock(&root_mutex);\r
        return(st);\r
 }\r
 \r
@@ -390,12 +407,21 @@ void rrd_stats_next(RRD_STATS *st)
 {\r
        struct timeval *now;\r
 \r
+       pthread_mutex_lock(&st->mutex);\r
+\r
        // st->last_entry should never be outside the array\r
        // or, the parallel threads may end up crashing\r
        st->last_entry = ((st->last_entry + 1) >= st->entries) ? 0 : st->last_entry + 1;\r
 \r
        now = &st->times[st->last_entry];\r
        gettimeofday(now, NULL);\r
+\r
+       // leave mutex locked\r
+}\r
+\r
+void rrd_stats_done(RRD_STATS *st)\r
+{\r
+       pthread_mutex_unlock(&st->mutex);\r
 }\r
 \r
 void rrd_stats_dimension_set(RRD_STATS *st, const char *dimension, void *data)\r
@@ -434,6 +460,8 @@ void rrd_stats_dimension_set(RRD_STATS *st, const char *dimension, void *data)
 \r
 size_t rrd_stats_json(RRD_STATS *st, char *b, size_t length, size_t entries_to_show, size_t group_count, int group_method)\r
 {\r
+       pthread_mutex_lock(&st->mutex);\r
+\r
        // check the options\r
        if(entries_to_show <= 0) entries_to_show = 1;\r
        if(group_count <= 0) group_count = 1;\r
@@ -459,8 +487,10 @@ size_t rrd_stats_json(RRD_STATS *st, char *b, size_t length, size_t entries_to_s
        for( rd = st->dimensions ; rd ; rd = rd->next)\r
                dimensions++;\r
 \r
-       if(!dimensions)\r
+       if(!dimensions) {\r
+               pthread_mutex_unlock(&st->mutex);\r
                return sprintf(b, "No dimensions yet.");\r
+       }\r
 \r
        // temporary storage to keep track of group values and counts\r
        long long group_values[dimensions];\r
@@ -518,7 +548,8 @@ size_t rrd_stats_json(RRD_STATS *st, char *b, size_t length, size_t entries_to_s
                        struct tm *tm = localtime(&st->times[t].tv_sec);\r
                        if(!tm) { error("localtime() failed."); continue; }\r
 \r
-                       strftime(dtm, 200, "[%H, %M, %S, 0]", tm);\r
+                       // strftime(dtm, 200, "[%Y, %m, %d, %H, %M, %S, 0]", tm); // datetime\r
+                       strftime(dtm, 200, "[%H, %M, %S, 0]", tm); // timeofday\r
                        i += sprintf(&b[i], "%s         {\"c\":[{\"v\":%s},", printed?"]},\n":"", dtm);\r
 \r
                        printed++;\r
@@ -567,6 +598,7 @@ size_t rrd_stats_json(RRD_STATS *st, char *b, size_t length, size_t entries_to_s
        if(printed) i += sprintf(&b[i], "]}");\r
        i += sprintf(&b[i], "\n ]\n}\n");\r
 \r
+       pthread_mutex_unlock(&st->mutex);\r
        return(i);\r
 }\r
 \r
@@ -1307,35 +1339,9 @@ void *socket_listen_main(void *ptr)
 // ----------------------------------------------------------------------------\r
 // /proc/net/dev processor\r
 \r
-void update_iface_history(char *name, unsigned long long rbytes, unsigned long long tbytes) {\r
-\r
-       RRD_STATS *st = rrd_stats_find(name);\r
-       if(!st) {\r
-               st = rrd_stats_create(name, save_history);\r
-               if(!st) {\r
-                       error("Cannot create RRD_STATS for interface %s.", name);\r
-                       return;\r
-               }\r
-\r
-               if(!rrd_stats_dimension_add(st, "received", sizeof(unsigned long long), 8, 1024)) {\r
-                       error("Cannot add RRD_STATS dimension %s.", "received");\r
-                       return;\r
-               }\r
-\r
-               if(!rrd_stats_dimension_add(st, "sent", sizeof(unsigned long long), 8, 1024)) {\r
-                       error("Cannot add RRD_STATS dimension %s.", "sent");\r
-                       return;\r
-               }\r
-       }\r
-\r
-       rrd_stats_next(st);\r
-       rrd_stats_dimension_set(st, "received", &rbytes);\r
-       rrd_stats_dimension_set(st, "sent", &tbytes);\r
-}\r
-\r
 int do_proc_net_dev() {\r
        char buffer[MAX_PROC_NET_DEV_LINE+1] = "";\r
-       char iface[MAX_PROC_NET_DEV_IFACE_NAME + 1] = "net.";\r
+       char name[MAX_PROC_NET_DEV_IFACE_NAME + 1] = "net.";\r
        unsigned long long rbytes, rpackets, rerrors, rdrops, rfifo, rframe, rcompressed, rmulticast;\r
        unsigned long long tbytes, tpackets, terrors, tdrops, tfifo, tcollisions, tcarrier, tcompressed;\r
        \r
@@ -1363,19 +1369,139 @@ int do_proc_net_dev() {
                \r
                // if(DEBUG) printf("%s\n", buffer);\r
                r = sscanf(buffer, "%s\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\n",\r
-                       &iface[4],\r
+                       &name[4],\r
                        &rbytes, &rpackets, &rerrors, &rdrops, &rfifo, &rframe, &rcompressed, &rmulticast,\r
                        &tbytes, &tpackets, &terrors, &tdrops, &tfifo, &tcollisions, &tcarrier, &tcompressed);\r
                if(r == EOF) break;\r
                if(r != 17) error("Cannot read /proc/net/dev line. Expected 17 params, read %d.", r);\r
-               else update_iface_history(iface, rbytes, tbytes);\r
+               else {\r
+                       RRD_STATS *st = rrd_stats_find(name);\r
+\r
+                       if(!st) {\r
+                               st = rrd_stats_create(name, save_history);\r
+                               if(!st) {\r
+                                       error("Cannot create RRD_STATS for interface %s.", name);\r
+                                       continue;\r
+                               }\r
+\r
+                               if(!rrd_stats_dimension_add(st, "sent", sizeof(unsigned long long), 8, 1024))\r
+                                       error("Cannot add RRD_STATS dimension %s.", "sent");\r
+\r
+                               if(!rrd_stats_dimension_add(st, "received", sizeof(unsigned long long), 8, 1024))\r
+                                       error("Cannot add RRD_STATS dimension %s.", "received");\r
+\r
+                       }\r
+                       else rrd_stats_next(st);\r
+\r
+                       rrd_stats_dimension_set(st, "received", &rbytes);\r
+                       rrd_stats_dimension_set(st, "sent", &tbytes);\r
+                       rrd_stats_done(st);\r
+               }\r
+       }\r
+       \r
+       fclose(fp);\r
+       return 0;\r
+}\r
+\r
+int do_proc_diskstats() {\r
+       char buffer[MAX_PROC_DISKSTATS_LINE+1] = "";\r
+       char name[MAX_PROC_DISKSTATS_DISK_NAME + 1] = "disk.";\r
+       //                               1      2             3            4       5       6              7             8        9           10     11\r
+       unsigned long long major, minor, reads, reads_merged, readsectors, readms, writes, writes_merged, writesectors, writems, currentios, iosms, wiosms;\r
+       \r
+       int r;\r
+       char *p;\r
+       \r
+       FILE *fp = fopen("/proc/diskstats", "r");\r
+       if(!fp) {\r
+               error("Cannot read /proc/diskstats.");\r
+               return 1;\r
+       }\r
+       \r
+       for(;1;) {\r
+               p = fgets(buffer, MAX_PROC_DISKSTATS_LINE, fp);\r
+               if(!p) break;\r
+               \r
+               // if(DEBUG) printf("%s\n", buffer);\r
+               r = sscanf(buffer, "%llu %llu %s %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",\r
+                       &major, &minor, &name[5],\r
+                       &reads, &reads_merged, &readsectors, &readms, &writes, &writes_merged, &writesectors, &writems, &currentios, &iosms, &wiosms\r
+               );\r
+               if(r == EOF) break;\r
+               if(r != 14) error("Cannot read /proc/diskstats line. Expected 14 params, read %d.", r);\r
+               else {\r
+                       switch(major) {\r
+                               case 1: // ram drives\r
+                                       continue;\r
+\r
+                               case 7: // loops\r
+                                       continue;\r
+\r
+                               case 8: // disks\r
+                                       if(minor % 16) continue; // partitions\r
+                                       break;\r
+\r
+                               case 9: // MDs\r
+                                       break;\r
+\r
+                               case 11: // CDs\r
+                                       continue;\r
+\r
+                               default:\r
+                                       continue;\r
+                       }\r
+\r
+                       RRD_STATS *st = rrd_stats_find(name);\r
+\r
+                       if(!st) {\r
+                               char ssfilename[FILENAME_MAX + 1];\r
+                               int sector_size = 512;\r
+\r
+                               sprintf(ssfilename, "/sys/block/%s/queue/hw_sector_size", &name[5]);\r
+                               FILE *fpss = fopen(ssfilename, "r");\r
+                               if(fpss) {\r
+                                       char ssbuffer[1025];\r
+                                       char *tmp = fgets(ssbuffer, 1024, fpss);\r
+\r
+                                       if(tmp) {\r
+                                               sector_size = atoi(tmp);\r
+                                               if(sector_size <= 0) {\r
+                                                       error("Invalid sector size %d for device %s in %s. Assuming 512.", sector_size, name, ssfilename);\r
+                                                       sector_size = 512;\r
+                                               }\r
+                                       }\r
+                                       else error("Cannot read data for sector size for device %s from %s. Assuming 512.", name, ssfilename);\r
+\r
+                                       fclose(fpss);\r
+                               }\r
+                               else error("Cannot read sector size for device %s from %s. Assuming 512.", name, ssfilename);\r
+\r
+                               st = rrd_stats_create(name, save_history);\r
+                               if(!st) {\r
+                                       error("Cannot create RRD_STATS for disk %s.", name);\r
+                                       continue;\r
+                               }\r
+\r
+                               if(!rrd_stats_dimension_add(st, "reads", sizeof(unsigned long long), sector_size, 1024))\r
+                                       error("Cannot add RRD_STATS dimension %s.", "reads");\r
+\r
+                               if(!rrd_stats_dimension_add(st, "writes", sizeof(unsigned long long), sector_size, 1024))\r
+                                       error("Cannot add RRD_STATS dimension %s.", "writes");\r
+\r
+                       }\r
+                       else rrd_stats_next(st);\r
+\r
+                       rrd_stats_dimension_set(st, "reads", &readsectors);\r
+                       rrd_stats_dimension_set(st, "writes", &writesectors);\r
+                       rrd_stats_done(st);\r
+               }\r
        }\r
        \r
        fclose(fp);\r
        return 0;\r
 }\r
 \r
-void *proc_net_dev_main(void *ptr)\r
+void *proc_main(void *ptr)\r
 {\r
        struct timeval last, now, tmp;\r
 \r
@@ -1391,6 +1517,7 @@ void *proc_net_dev_main(void *ptr)
                debug(D_PROCNETDEV_LOOP, "PROCNETDEV: Last loop took %llu usec.", usec);\r
                \r
                do_proc_net_dev(usec);\r
+               do_proc_diskstats(usec);\r
                \r
                // find the time to sleep in order to wait exactly update_every seconds\r
                gettimeofday(&tmp, NULL);\r
@@ -1481,20 +1608,20 @@ int main(int argc, char **argv)
                silent = 1;\r
        }\r
 \r
-       pthread_t p_proc_net_dev;\r
-       int r_proc_net_dev;\r
+       pthread_t p_proc;\r
+       int r_proc;\r
 \r
        // spawn a child to collect data\r
-       r_proc_net_dev  = pthread_create(&p_proc_net_dev, NULL, proc_net_dev_main, NULL);\r
+       r_proc  = pthread_create(&p_proc, NULL, proc_main, NULL);\r
 \r
        // the main process - the web server listener\r
        //sleep(1);\r
        socket_listen_main(NULL);\r
 \r
        // wait for the childs to finish\r
-       pthread_join(p_proc_net_dev,  NULL);\r
+       pthread_join(p_proc,  NULL);\r
 \r
-       printf("PROC NET DEV  thread returns: %d\n", r_proc_net_dev);\r
+       printf("PROC NET DEV  thread returns: %d\n", r_proc);\r
 \r
        exit(0);\r
 }\r
index a95c075b9861b15d26c89c683ea86a5957109920..2f18b88d358c099e9ca9697d8ae42e8ef00bb76e 100644 (file)
@@ -8,7 +8,7 @@
                if(index >= charts.length) return;
                
                var jsonData = $.ajax({
-                       url: charts_urls[index],
+                       url: charts[index].url,
                        dataType:"json",
                        async: false,
                        cache: false
                if(!jsonData || jsonData.length == 0) return;
                
                // Create our data table out of JSON data loaded from server.
-               charts_data[index] = null;
-               charts_data[index] = new google.visualization.DataTable(jsonData);
+               charts[index].data = new google.visualization.DataTable(jsonData);
                
                // Instantiate and draw our chart, passing in some options.
-               if(!charts[index]) {
-                       console.log('Creating new chart for ' + charts_urls[index]);
-                       charts[index] = new google.visualization.AreaChart(document.getElementById(charts_divs[index]));
+               if(!charts[index].chart) {
+                       console.log('Creating new chart for ' + charts[index].url);
+                       charts[index].chart = new google.visualization.AreaChart(document.getElementById(charts[index].div));
                }
                
-               var width = charts_widths[index];
+               var width = charts[index].width;
                if(width == 0) width = (window.innerWidth - 40) / 2;
                if(width <= 10) width = (window.innerWidth - 40) / width;
                if(width < 200) width = 200;
                
-               var height = charts_heights[index];
+               var height = charts[index].height;
                if(height == 0) height = (window.innerHeight - 20) / 2;
                if(height <= 10) height = (window.innerHeight - 20) / height;
                if(height < 100) height = 100;
                var hAxisTitle = null;
                var vAxisTitle = null;
                if(height >= 200) hAxisTitle = "Time of Day";
-               if(width >= 400) vAxisTitle = "Bandwidth in kbps";
+               if(width >= 400) vAxisTitle = charts[index].vtitle;
                
-               if(charts[index]) charts[index].draw(charts_data[index], {width: width, height: height, title: charts_titles[index], hAxis: {title: hAxisTitle}, vAxis: {title: vAxisTitle, minValue: 10}});
-               else console.log('Cannot create chart for ' + charts_urls[index]);
+               if(charts[index].chart) charts[index].chart.draw(charts[index].data, {width: width, height: height, title: charts[index].title, hAxis: {title: hAxisTitle}, vAxis: {title: vAxisTitle, minValue: 10}});
+               else console.log('Cannot create chart for ' + charts[index].url);
        }
        
        var charts = new Array();
-       var charts_data = new Array();
-       var charts_names = new Array();
-       var charts_divs = new Array();
-       var charts_widths = new Array();
-       var charts_heights = new Array();
-       var charts_urls = new Array();
-       var charts_titles = new Array();
-       
-       function drawChart(name, div, width, height, jsonurl, title) {
+       function drawChart(name, div, width, height, jsonurl, title, vtitle) {
                var i;
                
-               for(i = 0; i < charts_names.length; i++) //>
-                       if(charts_names[i] == name) break;
+               for(i = 0; i < charts.length; i++) //>
+                       if(charts[i].name == name) break;
                
-               if(i >= charts_names.length) { //>
+               if(i >= charts.length) { //>
                        console.log('Creating new objects for chart ' + name);
-                       charts[i] = null;
-                       charts_data[i] = null;
-                       charts_names[i] = name;
-                       charts_divs[i] = div;
-                       charts_widths[i] = width;
-                       charts_heights[i] = height;
-                       charts_urls[i] = jsonurl;
-                       charts_titles[i] = title;
+                       charts[i] = [];
+                       charts[i].chart = null;
+                       charts[i].data = null;
+                       charts[i].name = name;
+                       charts[i].div = div;
+                       charts[i].width = width;
+                       charts[i].height = height;
+                       charts[i].url = jsonurl;
+                       charts[i].title = title;
+                       charts[i].vtitle = vtitle;
                }
                
                try {
index 783ff13030210ff7224f6d46fe2cc8bb86d3f7d1..54bc702a9aa66b663019b212a50a1180e8b4f04a 100755 (executable)
@@ -167,21 +167,37 @@ cat >${data}/index.html <<EOF
                
                // EDIT: add one line per interface you have
                // EDIT: 
-               // EDIT:   name    div id                    json data           graph title
-               // EDIT: --------------------------------------------------------------------------------
+               // EDIT:   name    div id                    json data           graph                                  vertical axis title
+               // EDIT: --------------------------------------------------------------------------------------------------
 EOF
 
 for x in $all
 do
        y=`echo "$x" | tr ".-" "__"`
-       eval "NETDATA_TITLE_${y}=\${NETDATA_TITLE_${y}:-Live Network Usage for ${x}}"
+
+       title=
+       vtitle=
+       case "$x" in
+               net.*)
+                       title="Live Network Usage for "
+                       vtitle="bandwidth in kilobits/s"
+                       ;;
+
+               disk.*)
+                       title="Live Disk I/O for "
+                       vtitle="I/O in kilobytes/s"
+                       ;;
+
+       esac
+
+       eval "NETDATA_TITLE_${y}=\${NETDATA_TITLE_${y}:-${x}}"
        eval "t=\${NETDATA_TITLE_${y}}"
        
        eval "p=\${NETDATA_PRIORITY_${y}}"
        if [ ! "$p" = "IGNORE" ]
        then
                cat >>${data}/index.html <<EOF2
-               drawChart('${x}', '${y}_div', width, height, "data/${x}/${NETDATA_CONFIG_HISTORY_POINTS}/${NETDATA_CONFIG_UPDATE_EVERY}/average/", "${t}");
+               drawChart('${x}', '${y}_div', width, height, "data/${x}/${NETDATA_CONFIG_HISTORY_POINTS}/${NETDATA_CONFIG_UPDATE_EVERY}/average/", "${title}${t} (${x})", "${vtitle}");
 EOF2
        fi
 done
@@ -192,9 +208,9 @@ if [ $count -gt 4 ]
 then
        ref=1
        tim=$[1000 * NETDATA_CONFIG_UPDATE_EVERY / count]
-       if [ $tim -lt $[333 * NETDATA_CONFIG_UPDATE_EVERY] ]
+       if [ $tim -lt $[200 * NETDATA_CONFIG_UPDATE_EVERY] ]
        then
-               tim=$[333 * NETDATA_CONFIG_UPDATE_EVERY]
+               tim=$[200 * NETDATA_CONFIG_UPDATE_EVERY]
        fi
 fi
 
@@ -202,18 +218,22 @@ cat >>${data}/index.html <<EOF3
        }
        
        var refreshCount = 0;
-        function myChartsRefresh() {
+       function myChartsRefresh() {
                refreshCount++;
                if(refreshCount > $NETDATA_CONFIG_RELOAD_EVERY) location.reload();
-               
+
                // refresh up to $ref charts per second
-                refreshCharts($ref);
-        }
+               refreshCharts($ref);
+       }
        
-        // EDIT: how often the charts are updated, in milliseconds
-        setInterval(myChartsRefresh, $tim);
+       setInterval(myChartsRefresh, $tim);
+
+       window.onresize = function(event) {
+               refreshCharts(999999);
+       };
        </script>
-</head>
+
+       </head>
 
 <body>
        <!--