]> arthur.barton.de Git - netdata.git/commitdiff
full apps monitor plugin writen in C
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 4 May 2014 23:00:44 +0000 (02:00 +0300)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Sun, 4 May 2014 23:00:44 +0000 (02:00 +0300)
apps_plugin.c [new file with mode: 0755]
netdata.c
netdata.start

diff --git a/apps_plugin.c b/apps_plugin.c
new file mode 100755 (executable)
index 0000000..45ca979
--- /dev/null
@@ -0,0 +1,653 @@
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <time.h>\r
+#include <unistd.h>\r
+#include <sys/types.h>\r
+#include <sys/time.h>\r
+#include <sys/wait.h>\r
+\r
+#include <sys/resource.h>\r
+#include <sys/stat.h>\r
+\r
+#include <errno.h>\r
+#include <stdarg.h>\r
+#include <locale.h>\r
+#include <ctype.h>\r
+#include <fcntl.h>\r
+\r
+#include <malloc.h>\r
+#include <inttypes.h>\r
+#include <dirent.h>\r
+#include <arpa/inet.h>\r
+\r
+#define MAX_COMPARE_NAME 15\r
+\r
+\r
+#define add_childs 1ULL\r
+\r
+unsigned long long Hertz = 1;\r
+\r
+long pid_max = 32768;\r
+int debug = 0;\r
+\r
+struct wanted {\r
+       char compare[MAX_COMPARE_NAME + 3];\r
+       char id[FILENAME_MAX+1];\r
+       char name[FILENAME_MAX+1];\r
+\r
+       unsigned long long minflt;\r
+       unsigned long long cminflt;\r
+       unsigned long long majflt;\r
+       unsigned long long cmajflt;\r
+       unsigned long long utime;\r
+       unsigned long long stime;\r
+       unsigned long long cutime;\r
+       unsigned long long cstime;\r
+       unsigned long long num_threads;\r
+       unsigned long long rss;\r
+\r
+       int merge_count;\r
+\r
+       struct wanted *target;  // the one that will be reported to netdata\r
+       struct wanted *next;\r
+} *wanted_root = NULL;\r
+\r
+int update_every = 1;\r
+\r
+struct wanted *add_wanted(const char *id, struct wanted *target)\r
+{\r
+       struct wanted *w = calloc(sizeof(struct wanted), 1);\r
+       if(!w) {\r
+               fprintf(stderr, "Cannot allocate %ld bytes of memory\n", sizeof(struct wanted));\r
+               return NULL;\r
+       }\r
+\r
+       strncpy(w->id, id, FILENAME_MAX);\r
+       strncpy(w->name, id, FILENAME_MAX);\r
+       snprintf(w->compare, MAX_COMPARE_NAME+1, "(%.*s)", MAX_COMPARE_NAME, id);\r
+       w->target = target;\r
+\r
+       w->next = wanted_root;\r
+       wanted_root = w;\r
+\r
+       if(debug) fprintf(stderr, "Adding hook for process '%s', compare '%s' on target '%s'\n", w->id, w->compare, w->target?w->target->id:"");\r
+\r
+       return w;\r
+}\r
+\r
+void parse_args(int argc, char **argv)\r
+{\r
+       int i = 1;\r
+\r
+       debug = 0;\r
+       if(strcmp(argv[i], "debug") == 0) {\r
+               debug = 1;\r
+               i++;\r
+       }\r
+\r
+       update_every = atoi(argv[i++]);\r
+       if(update_every == 0) {\r
+               i = 1;\r
+               update_every = 1;\r
+       }\r
+\r
+       for(; i < argc ; i++) {\r
+               struct wanted *w = NULL;\r
+               char *s = argv[i];\r
+               char *t;\r
+\r
+               while((t = strsep(&s, " "))) {\r
+                       if(w && strcmp(t, "as") == 0 && s && *s) {\r
+                               strncpy(w->name, s, FILENAME_MAX);\r
+                               if(debug) fprintf(stderr, "Setting dimension name to '%s' on target '%s'\n", w->name, w->id);\r
+                               break;\r
+                       }\r
+\r
+                       struct wanted *n = add_wanted(t, w);\r
+                       if(!w) w = n;\r
+               }\r
+       }\r
+}\r
+\r
+// see: man proc\r
+struct pid_stat {\r
+       int32_t pid;\r
+       char comm[FILENAME_MAX+1];\r
+       char state;\r
+       int32_t ppid;\r
+       int32_t pgrp;\r
+       int32_t session;\r
+       int32_t tty_nr;\r
+       int32_t tpgid;\r
+       uint64_t flags;\r
+       unsigned long long minflt;\r
+       unsigned long long cminflt;\r
+       unsigned long long majflt;\r
+       unsigned long long cmajflt;\r
+       unsigned long long utime;\r
+       unsigned long long stime;\r
+       unsigned long long cutime;\r
+       unsigned long long cstime;\r
+       int64_t priority;\r
+       int64_t nice;\r
+       int32_t num_threads;\r
+       int64_t itrealvalue;\r
+       unsigned long long starttime;\r
+       unsigned long long vsize;\r
+       unsigned long long rss;\r
+       unsigned long long rsslim;\r
+       unsigned long long starcode;\r
+       unsigned long long endcode;\r
+       unsigned long long startstack;\r
+       unsigned long long kstkesp;\r
+       unsigned long long kstkeip;\r
+       uint64_t signal;\r
+       uint64_t blocked;\r
+       uint64_t sigignore;\r
+       uint64_t sigcatch;\r
+       uint64_t wchan;\r
+       uint64_t nswap;\r
+       uint64_t cnswap;\r
+       int32_t exit_signal;\r
+       int32_t processor;\r
+       uint32_t rt_priority;\r
+       uint32_t policy;\r
+       unsigned long long delayacct_blkio_ticks;\r
+       uint64_t guest_time;\r
+       int64_t cguest_time;\r
+\r
+       int childs;     // number of processes directly referencing this\r
+       int updated;\r
+       int merged;\r
+       int new_entry;\r
+       int merge_count;\r
+       struct wanted *target;\r
+       struct pid_stat *parent;\r
+       struct pid_stat *prev;\r
+       struct pid_stat *next;\r
+} *root = NULL;\r
+\r
+struct pid_stat **all_pids;\r
+\r
+#define PID_STAT_LINE_MAX 4096\r
+\r
+struct pid_stat *get_entry(pid_t pid)\r
+{\r
+       if(all_pids[pid]) {\r
+               all_pids[pid]->new_entry = 0;\r
+               return all_pids[pid];\r
+       }\r
+\r
+       all_pids[pid] = calloc(sizeof(struct pid_stat), 1);\r
+       if(!all_pids[pid]) {\r
+               fprintf(stderr, "Cannot allocate %lu bytes of memory", sizeof(struct pid_stat));\r
+               return NULL;\r
+       }\r
+\r
+       if(root) root->prev = all_pids[pid];\r
+       all_pids[pid]->next = root;\r
+       root = all_pids[pid];\r
+\r
+       all_pids[pid]->new_entry = 1;\r
+\r
+       return all_pids[pid];\r
+}\r
+\r
+void del_entry(pid_t pid)\r
+{\r
+       if(!all_pids[pid]) return;\r
+\r
+       if(root == all_pids[pid]) root = all_pids[pid]->next;\r
+       if(all_pids[pid]->next) all_pids[pid]->next->prev = all_pids[pid]->prev;\r
+       if(all_pids[pid]->prev) all_pids[pid]->prev->next = all_pids[pid]->next;\r
+\r
+       free(all_pids[pid]);\r
+       all_pids[pid] = NULL;\r
+}\r
+\r
+int update_from_proc(void)\r
+{\r
+       char buffer[PID_STAT_LINE_MAX + 1];\r
+       char filename[FILENAME_MAX+1];\r
+       DIR *dir = opendir("/proc");\r
+       if(!dir) return 0;\r
+\r
+       struct dirent *file = NULL;\r
+       struct pid_stat *p = NULL;\r
+\r
+       // mark them all as un-updated\r
+       for(p = root; p ; p = p->next) {\r
+               p->parent = NULL;\r
+               p->updated = 0;\r
+               p->childs = 0;\r
+               p->merged = 0;\r
+               p->new_entry = 0;\r
+               p->merge_count = 0;\r
+       }\r
+\r
+       while((file = readdir(dir))) {\r
+               char *endptr = file->d_name;\r
+               pid_t pid = strtoul(file->d_name, &endptr, 10);\r
+               if(pid <= 0 || pid > pid_max || endptr == file->d_name || *endptr != '\0') continue;\r
+\r
+               snprintf(filename, FILENAME_MAX, "/proc/%s/stat", file->d_name);\r
+\r
+               int fd = open(filename, O_RDONLY);\r
+               if(fd == -1) {\r
+                       if(errno != ENOENT) fprintf(stderr, "Cannot open file '%s' for reading (%s).\n", filename, strerror(errno));\r
+                       continue;\r
+               }\r
+\r
+               int bytes = read(fd, buffer, PID_STAT_LINE_MAX);\r
+               if(bytes == -1) {\r
+                       fprintf(stderr, "Cannot read from file '%s' (%s).\n", filename, strerror(errno));\r
+                       close(fd);\r
+                       continue;\r
+               }\r
+\r
+               close(fd);\r
+               if(bytes < 100) continue;\r
+\r
+               p = get_entry(pid);\r
+               if(!p) continue;\r
+\r
+               int parsed = sscanf(buffer,\r
+                       "%d %s %c"                                                      // pid, comm, state\r
+                       " %d %d %d %d %d"                                       // ppid, pgrp, session, tty_nr, tpgid\r
+                       " %lu %llu %llu %llu %llu"                      // flags, minflt, cminflt, majflt, cmajflt\r
+                       " %llu %llu %llu %llu"                          // utime, stime, cutime, cstime\r
+                       " %ld %ld"                                                      // priority, nice\r
+                       " %d"                                                           // num_threads\r
+                       " %ld"                                                          // itrealvalue\r
+                       " %llu"                                                         // starttime\r
+                       " %llu"                                                         // vsize\r
+                       " %llu"                                                         // rss\r
+                       " %llu %llu %llu %llu %llu %llu"        // rsslim, starcode, endcode, startstack, kstkesp, kstkeip\r
+                       " %lu %lu %lu %lu"                                      // signal, blocked, sigignore, sigcatch\r
+                       " %lu %lu %lu"                                          // wchan, nswap, cnswap\r
+                       " %d %d"                                                        // exit_signal, processor\r
+                       " %u %u"                                                        // rt_priority, policy\r
+                       " %llu %lu %ld"\r
+                       , &p->pid, p->comm, &p->state\r
+                       , &p->ppid, &p->pgrp, &p->session, &p->tty_nr, &p->tpgid\r
+                       , &p->flags, &p->minflt, &p->cminflt, &p->majflt, &p->cmajflt\r
+                       , &p->utime, &p->stime, &p->cutime, &p->cstime\r
+                       , &p->priority, &p->nice\r
+                       , &p->num_threads\r
+                       , &p->itrealvalue\r
+                       , &p->starttime\r
+                       , &p->vsize\r
+                       , &p->rss\r
+                       , &p->rsslim, &p->starcode, &p->endcode, &p->startstack, &p->kstkesp, &p->kstkeip\r
+                       , &p->signal, &p->blocked, &p->sigignore, &p->sigcatch\r
+                       , &p->wchan, &p->nswap, &p->cnswap\r
+                       , &p->exit_signal, &p->processor\r
+                       , &p->rt_priority, &p->policy\r
+                       , &p->delayacct_blkio_ticks, &p->guest_time, &p->cguest_time\r
+                       );\r
+\r
+               if(parsed < 39) fprintf(stderr, "file %s gave %d results (expected 44)\n", filename, parsed);\r
+\r
+               // check if it is wanted\r
+               // we do this only once, the first time this pid is loaded\r
+               if(p->new_entry) {\r
+                       if(debug) fprintf(stderr, "\tJust added %s\n", p->comm);\r
+\r
+                       struct wanted *w;\r
+                       for(w = wanted_root; w ; w = w->next) {\r
+                               // if(debug) fprintf(stderr, "\t\tcomparing '%s' with '%s'\n", w->compare, p->comm);\r
+\r
+                               if(strcmp(w->compare, p->comm) == 0) {\r
+                                       if(w->target) p->target = w->target;\r
+                                       else p->target = w;\r
+\r
+                                       if(debug) fprintf(stderr, "\t\t%s linked to target %s\n", p->comm, p->target->name);\r
+                               }\r
+                       }\r
+               }\r
+\r
+               // just a few checks\r
+               if(p->ppid < 1 || p->ppid > pid_max) p->ppid = 1;\r
+\r
+               // mark it as updated\r
+               p->updated = 1;\r
+       }\r
+       closedir(dir);\r
+\r
+       // cleanup all un-updated\r
+       int c;\r
+       for(p = root, c = 0; p ; c++) {\r
+               if(!p->updated) {\r
+                       pid_t r = p->pid;\r
+                       p = p->next;\r
+                       del_entry(r);\r
+               }\r
+               else p = p->next;\r
+       }\r
+\r
+       if(debug) fprintf(stderr, "There are %d processes active\n", c);\r
+\r
+       return 1;\r
+}\r
+\r
+void merge_processes(void)\r
+{\r
+       struct pid_stat *p = NULL;\r
+\r
+       // link all parents and update childs\r
+       for(p = root; p ; p = p->next) {\r
+               if(p->ppid > 1 && p->ppid <= pid_max && all_pids[p->ppid]) {\r
+                       if(debug) fprintf(stderr, "\tParent of %d %s is %d %s\n", p->pid, p->comm, p->ppid, all_pids[p->ppid]->comm);\r
+                       \r
+                       p->parent = all_pids[p->ppid];\r
+                       if(!p->parent) {\r
+                               if(debug) fprintf(stderr, "\t\tpid %d %s states parent %d, but the later does not exist.\n", p->pid, p->comm, p->ppid);\r
+                               continue;\r
+                       }\r
+\r
+                       p->parent->childs++;\r
+               }\r
+       }\r
+\r
+       // find all the procs with 0 childs and update their parents\r
+       // continue, until nothing more can be done.\r
+       int found = 1;\r
+       for( ; found ; ) {\r
+               found = 0;\r
+               for(p = root; p ; p = p->next) {\r
+                       if(!p->childs && !p->merged && p->parent && p->parent->childs) {\r
+                               if(debug) fprintf(stderr, "\tMerging %d %s to %d %s (count: %d)\n", p->pid, p->comm, p->ppid, all_pids[p->ppid]->comm, p->parent->merge_count+1);\r
+\r
+                               p->parent->minflt += p->minflt;\r
+                               p->parent->majflt += p->majflt;\r
+                               p->parent->utime += p->utime;\r
+                               p->parent->stime += p->stime;\r
+\r
+                               p->parent->cminflt += p->cminflt;\r
+                               p->parent->cmajflt += p->cmajflt;\r
+                               p->parent->cutime += p->cutime;\r
+                               p->parent->cstime += p->cstime;\r
+\r
+                               p->parent->num_threads += p->num_threads;\r
+                               p->parent->rss += p->rss;\r
+\r
+                               p->parent->childs--;\r
+                               p->merged = 1;\r
+\r
+                               p->parent->merge_count++;\r
+\r
+                               // the parent inherits the child's target, if it does not have a target itself\r
+                               if(p->target && !p->parent->target) {\r
+                                       p->parent->target = p->target;\r
+                                       if(debug) fprintf(stderr, "\t\ttarget %s is inherited from %d %s by its child %d %s.\n", p->target->name, p->parent->pid, p->parent->comm, p->pid, p->comm);\r
+                               }\r
+\r
+                               found++;\r
+                       }\r
+               }\r
+               if(debug) fprintf(stderr, "Merged %d processes\n", found);\r
+       }\r
+\r
+       if(debug) {\r
+               fprintf(stderr, "Root level processes: \n");\r
+               for(p = root, found = 0; p ; p = p->next) {\r
+                       if(!p->childs && !p->merged && !p->parent) {\r
+                               fprintf(stderr, "\t\t%s %d on target %s\n", p->comm, p->pid, p->target?p->target->name:"-");\r
+                       }\r
+               }\r
+       }\r
+\r
+       // zero the targets\r
+       struct wanted *w;\r
+       for (w = wanted_root; w ; w = w->next) {\r
+               w->minflt = 0;\r
+               w->majflt = 0;\r
+               w->utime = 0;\r
+               w->stime = 0;\r
+               w->cminflt = 0;\r
+               w->cmajflt = 0;\r
+               w->cutime = 0;\r
+               w->cstime = 0;\r
+               w->num_threads = 0;\r
+               w->rss = 0;\r
+               w->merge_count = 0;\r
+       }\r
+\r
+       // concentrate everything on the targets\r
+       for(p = root; p ; p = p->next) {\r
+               if(!p->childs && !p->merged && !p->parent && p->target) {\r
+                       p->target->minflt += p->minflt;\r
+                       p->target->majflt += p->majflt;\r
+                       p->target->utime += p->utime;\r
+                       p->target->stime += p->stime;\r
+                       p->target->cminflt += p->cminflt;\r
+                       p->target->cmajflt += p->cmajflt;\r
+                       p->target->cutime += p->cutime;\r
+                       p->target->cstime += p->cstime;\r
+                       p->target->num_threads += p->num_threads;\r
+                       p->target->rss += p->rss;\r
+\r
+                       p->target->merge_count++;\r
+                       if(debug) fprintf(stderr, "\tAgregating %s pid %d on %s (count: %d)\n", p->comm, p->pid, p->target->name, p->target->merge_count);\r
+               }\r
+       }\r
+}\r
+\r
+\r
+void show_dimensions(void)\r
+{\r
+       struct wanted *w;\r
+\r
+       fprintf(stdout, "BEGIN apps.cpu\n");\r
+       for (w = wanted_root; w ; w = w->next) {\r
+               if(w->target) continue;\r
+\r
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + w->stime + (w->cutime * add_childs) + (w->cstime * add_childs));\r
+       }\r
+       fprintf(stdout, "END\n");\r
+\r
+       fprintf(stdout, "BEGIN apps.cpu_user\n");\r
+       for (w = wanted_root; w ; w = w->next) {\r
+               if(w->target) continue;\r
+\r
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + (w->cutime * add_childs));\r
+       }\r
+       fprintf(stdout, "END\n");\r
+\r
+       fprintf(stdout, "BEGIN apps.cpu_system\n");\r
+       for (w = wanted_root; w ; w = w->next) {\r
+               if(w->target) continue;\r
+\r
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->stime + (w->cstime * add_childs));\r
+       }\r
+       fprintf(stdout, "END\n");\r
+\r
+       fprintf(stdout, "BEGIN apps.threads\n");\r
+       for (w = wanted_root; w ; w = w->next) {\r
+               if(w->target) continue;\r
+\r
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->num_threads);\r
+       }\r
+       fprintf(stdout, "END\n");\r
+\r
+       fprintf(stdout, "BEGIN apps.rss\n");\r
+       for (w = wanted_root; w ; w = w->next) {\r
+               if(w->target) continue;\r
+\r
+               fprintf(stdout, "SET %s = %llu\n", w->name, (unsigned long long)w->rss);\r
+       }\r
+       fprintf(stdout, "END\n");\r
+\r
+       fprintf(stdout, "BEGIN apps.minor_faults\n");\r
+       for (w = wanted_root; w ; w = w->next) {\r
+               if(w->target) continue;\r
+\r
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->minflt + (w->cminflt * add_childs));\r
+       }\r
+       fprintf(stdout, "END\n");\r
+\r
+       fprintf(stdout, "BEGIN apps.major_faults\n");\r
+       for (w = wanted_root; w ; w = w->next) {\r
+               if(w->target) continue;\r
+\r
+               fprintf(stdout, "SET %s = %llu\n", w->name, w->majflt + (w->cmajflt * add_childs));\r
+       }\r
+       fprintf(stdout, "END\n");\r
+\r
+       fflush(stdout);\r
+}\r
+\r
+void show_charts(void)\r
+{\r
+       struct wanted *w;\r
+\r
+       fprintf(stdout, "CHART apps.cpu '' 'Applications CPU Time' 'cpu time %%' apps apps stacked 20001 %d\n", update_every);\r
+       for (w = wanted_root; w ; w = w->next) {\r
+               if(w->target) continue;\r
+\r
+               fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz);\r
+       }\r
+\r
+       fprintf(stdout, "CHART apps.rss '' 'Applications Memory' 'MB' apps apps stacked 20002 %d\n", update_every);\r
+       for (w = wanted_root; w ; w = w->next) {\r
+               if(w->target) continue;\r
+\r
+               fprintf(stdout, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L);\r
+       }\r
+\r
+       fprintf(stdout, "CHART apps.threads '' 'Applications Threads' 'threads' apps apps stacked 20005 %d\n", update_every);\r
+       for (w = wanted_root; w ; w = w->next) {\r
+               if(w->target) continue;\r
+\r
+               fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);\r
+       }\r
+\r
+       fprintf(stdout, "CHART apps.cpu_user '' 'Applications CPU User Time' 'cpu time %%' apps none stacked 20020 %d\n", update_every);\r
+       for (w = wanted_root; w ; w = w->next) {\r
+               if(w->target) continue;\r
+\r
+               fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz);\r
+       }\r
+\r
+       fprintf(stdout, "CHART apps.cpu_system '' 'Applications CPU System Time' 'cpu time %%' apps none stacked 20021 %d\n", update_every);\r
+       for (w = wanted_root; w ; w = w->next) {\r
+               if(w->target) continue;\r
+\r
+               fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz);\r
+       }\r
+\r
+       fprintf(stdout, "CHART apps.major_faults '' 'Applications Major Page Faults' 'page faults/s' apps apps stacked 20010 %d\n", update_every);\r
+       for (w = wanted_root; w ; w = w->next) {\r
+               if(w->target) continue;\r
+\r
+               fprintf(stdout, "DIMENSION %s '' incremental 1 1\n", w->name);\r
+       }\r
+\r
+       fprintf(stdout, "CHART apps.minor_faults '' 'Applications Minor Page Faults' 'page faults/s' apps apps stacked 20011 %d\n", update_every);\r
+       for (w = wanted_root; w ; w = w->next) {\r
+               if(w->target) continue;\r
+\r
+               fprintf(stdout, "DIMENSION %s '' incremental 1 1\n", w->name);\r
+       }\r
+\r
+       fflush(stdout);\r
+}\r
+\r
+unsigned long long get_hertz(void)\r
+{\r
+       unsigned long long hz = 1;\r
+\r
+#ifdef _SC_CLK_TCK\r
+       if((hz = sysconf(_SC_CLK_TCK)) > 0) {\r
+               return hz;\r
+       }\r
+#endif\r
+\r
+#ifdef HZ\r
+       hz = (unsigned long long)HZ;    /* <asm/param.h> */\r
+#else\r
+       /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */\r
+       hz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;\r
+#endif\r
+\r
+       fprintf(stderr, "Unknown HZ value. Assuming %llu", hz);\r
+       return hz;\r
+}\r
+\r
+\r
+long get_pid_max(void)\r
+{\r
+       return 32768L;\r
+}\r
+\r
+unsigned long long usecdiff(struct timeval *now, struct timeval *last) {\r
+               return ((((now->tv_sec * 1000000ULL) + now->tv_usec) - ((last->tv_sec * 1000000ULL) + last->tv_usec)));\r
+}\r
+\r
+void mysleep(unsigned long long susec)\r
+{\r
+       while(susec) {\r
+               struct timeval t1, t2;\r
+\r
+               gettimeofday(&t1, NULL);\r
+\r
+               if(debug) fprintf(stderr, "Sleeping for %llu microseconds\n", susec);\r
+               usleep(susec);\r
+\r
+               gettimeofday(&t2, NULL);\r
+\r
+               unsigned long long diff = usecdiff(&t2, &t1);\r
+               \r
+               if(diff < susec) susec -= diff;\r
+               else susec = 0;\r
+       }\r
+}\r
+\r
+int main(int argc, char **argv)\r
+{\r
+       Hertz = get_hertz();\r
+       pid_max = get_pid_max();\r
+\r
+       parse_args(argc, argv);\r
+\r
+       all_pids = calloc(sizeof(struct pid_stat *), pid_max);\r
+       if(!all_pids) {\r
+               fprintf(stderr, "Cannot allocate %lu bytes of memory.\n", sizeof(struct pid_stat *) * pid_max);\r
+               printf("DISABLE\n");\r
+               exit(1);\r
+       }\r
+\r
+       show_charts();\r
+\r
+       unsigned long long counter = 1;\r
+       unsigned long long usec = 0, susec = 0;\r
+       struct timeval last, now;\r
+       gettimeofday(&last, NULL);\r
+\r
+       for(;1; counter++) {\r
+               if(!update_from_proc()) {\r
+                       fprintf(stderr, "Cannot allocate %lu bytes of memory.\n", sizeof(struct pid_stat *) * pid_max);\r
+                       printf("DISABLE\n");\r
+                       exit(1);\r
+               }\r
+\r
+               merge_processes();\r
+               show_dimensions();\r
+\r
+               if(debug) fprintf(stderr, "Done Loop No %llu\n", counter);\r
+               // if(counter == 1000) exit(0);\r
+\r
+               gettimeofday(&now, NULL);\r
+\r
+               usec = usecdiff(&now, &last) - susec;\r
+               if(debug) fprintf(stderr, "last loop took %llu usec (worked for %llu, sleeped for %llu).\n", usec + susec, usec, susec);\r
+\r
+               if(usec < (update_every * 1000000ULL)) susec = (update_every * 1000000ULL) - usec;\r
+               else susec = 0;\r
+\r
+               mysleep(susec);\r
+\r
+               bcopy(&now, &last, sizeof(struct timeval));\r
+       }\r
+}\r
index 92130d7f3e032565a0b7776107c6a8e3f0987150..01cb2d54bcf67c0a506441c0178e6da5723ecb5f 100644 (file)
--- a/netdata.c
+++ b/netdata.c
@@ -313,6 +313,9 @@ FILE *mypopen(const char *command, pid_t *pidptr)
        for(i = sysconf(_SC_OPEN_MAX); i > 0; i--)
                if(i != STDIN_FILENO && i != STDERR_FILENO && i != pipefd[PIPE_WRITE]) close(i);
 
+       // ignore all signals
+       for (i = 1 ; i < 65 ;i++) if(i != SIGSEGV) signal(i, SIG_DFL);
+
        // move the pipe to stdout
        if(pipefd[PIPE_WRITE] != STDOUT_FILENO) {
                dup2(pipefd[PIPE_WRITE], STDOUT_FILENO);
@@ -1161,7 +1164,7 @@ int rrd_stats_dimension_set(RRD_STATS *st, char *id, collected_number value)
        return 0;
 }
 
-unsigned long long rrd_stats_next(RRD_STATS *st)
+void rrd_stats_next(RRD_STATS *st)
 {
        pthread_mutex_lock(&st->mutex);
 
@@ -1180,10 +1183,10 @@ unsigned long long rrd_stats_next(RRD_STATS *st)
 
        pthread_mutex_unlock(&st->mutex);
 
-       return st->usec_since_last_update;
+       return;
 }
 
-void rrd_stats_done(RRD_STATS *st)
+unsigned long long rrd_stats_done(RRD_STATS *st)
 {
        pthread_mutex_lock(&st->mutex);
 
@@ -1218,7 +1221,7 @@ void rrd_stats_done(RRD_STATS *st)
                st->enabled = 0;
                pthread_mutex_unlock(&st->mutex);
                // rrd_stats_free(st);
-               return;
+               return st->update_every * 1000000ULL;
        }
 
        // calculate totals and count the dimensions
@@ -1372,12 +1375,15 @@ void rrd_stats_done(RRD_STATS *st)
                // store the time difference to the last entry
                st->timediff[st->current_entry] = st->usec_since_last_update;
        }
+       else st->usec_since_last_update = st->update_every * 1000000ULL;
 
        st->last_updated.tv_sec  = now.tv_sec;
        st->last_updated.tv_usec = now.tv_usec;
        st->counter++;
 
        pthread_mutex_unlock(&st->mutex);
+
+       return st->usec_since_last_update;
 }
 
 
@@ -5309,8 +5315,7 @@ void tc_device_commit(struct tc_device *d)
                        }
                }
                else {
-                       unsigned long long usec = rrd_stats_next(st);
-                       debug(D_TC_LOOP, "TC: Committing TC device '%s' after %llu usec", d->name, usec);
+                       rrd_stats_next(st);
 
                        if(strcmp(d->id, d->name) != 0) {
                                // update the device name with the new one
@@ -5599,7 +5604,7 @@ void *cpuidlejitter_main(void *ptr)
 #define PLUGINSD_CMD_MAX (FILENAME_MAX*2)
 #define PLUGINSD_LINE_MAX 1024
 
-struct chartd {
+struct plugind {
        char id[CONFIG_MAX_NAME+1];                     // config node id
 
        char filename[FILENAME_MAX+1];          // just the filename
@@ -5613,7 +5618,7 @@ struct chartd {
        int obsolete;
        int enabled;
 
-       struct chartd *next;
+       struct plugind *next;
 } *pluginsd_root = NULL;
 
 // like strsep() but:
@@ -5656,7 +5661,7 @@ char *qstrsep(char **ptr)
 
 void *pluginsd_worker_thread(void *arg)
 {
-       struct chartd *cd = (struct chartd *)arg;
+       struct plugind *cd = (struct plugind *)arg;
        char line[PLUGINSD_LINE_MAX + 1];
 
        while(1) {
@@ -5678,18 +5683,24 @@ void *pluginsd_worker_thread(void *arg)
 
                        if(!s || !*s) continue;
                        else if(!strcmp(s, "SET")) {
+                               char *t;
+                               while((t = strchr(p, '='))) *t = ' ';
+                               
                                char *dimension = qstrsep(&p);
-                               char *equal = qstrsep(&p);
                                char *value = qstrsep(&p);
 
-                               if(!dimension || !equal || *equal != '=' || !value) {
-                                       error("PLUGINSD: script %s is requesting a SET on chart '%s', like this: 'SET %s %s %s %s'", cd->fullfilename, st->id, dimension?dimension:"", equal?equal:"", value?value:"");
-                                       continue;
+                               if(!dimension || !*dimension || !value) {
+                                       error("PLUGINSD: script %s is requesting a SET on chart '%s', like this: 'SET %s = %s'. Disabling it.", cd->fullfilename, st->id, dimension?dimension:"", value?value:"");
+                                       cd->enabled = 0;
+                                       kill(cd->pid, SIGTERM);
+                                       break;
                                }
 
                                if(!st) {
-                                       error("PLUGINSD: script %s is requesting a SET, without a BEGIN", cd->fullfilename);
-                                       continue;
+                                       error("PLUGINSD: script %s is requesting a SET on dimension %s with value %s, without a BEGIN. Disabling it.", cd->fullfilename, dimension, value);
+                                       cd->enabled = 0;
+                                       kill(cd->pid, SIGTERM);
+                                       break;
                                }
 
                                if(st->debug) debug(D_PLUGINSD, "PLUGINSD: script %s is setting dimension %s/%s to %s", cd->fullfilename, st->id, dimension, value);
@@ -5700,25 +5711,39 @@ void *pluginsd_worker_thread(void *arg)
                        else if(!strcmp(s, "BEGIN")) {
                                char *id = qstrsep(&p);
                                if(!id) {
-                                       error("PLUGINSD: script %s is requesting a BEGIN without a chart id", cd->fullfilename);
-                                       continue;
+                                       error("PLUGINSD: script %s is requesting a BEGIN without a chart id. Disabling it.", cd->fullfilename);
+                                       cd->enabled = 0;
+                                       kill(cd->pid, SIGTERM);
+                                       break;
                                }
 
                                st = rrd_stats_find(id);
                                if(!st) {
-                                       error("PLUGINSD: script %s is requesting a BEGIN on chart '%s', which does not exist", cd->fullfilename, id);
-                                       continue;
+                                       error("PLUGINSD: script %s is requesting a BEGIN on chart '%s', which does not exist. Disabling it.", cd->fullfilename, id);
+                                       cd->enabled = 0;
+                                       kill(cd->pid, SIGTERM);
+                                       break;
                                }
                                if(st->counter) rrd_stats_next(st);
                        }
                        else if(!strcmp(s, "END")) {
                                if(!st) {
-                                       error("PLUGINSD: script %s is requesting an END, without a BEGIN", cd->fullfilename);
-                                       continue;
+                                       error("PLUGINSD: script %s is requesting an END, without a BEGIN. Disabling it.", cd->fullfilename);
+                                       cd->enabled = 0;
+                                       kill(cd->pid, SIGTERM);
+                                       break;
                                }
 
                                if(st->debug) debug(D_PLUGINSD, "PLUGINSD: script %s is requesting a END on chart %s", cd->fullfilename, st->id);
-                               rrd_stats_done(st);
+
+                               unsigned long long usec_since_last_update = rrd_stats_done(st);
+                               if(usec_since_last_update < (cd->update_every * 1000000ULL / 2)) {
+                                       error("PLUGINSD: script '%s' updates charts too frequently. Chart %s updated after %llu microseconds, expected %llu microseconds. Disabling it.", cd->fullfilename, st->id, usec_since_last_update, cd->update_every * 1000000ULL);
+                                       cd->enabled = 0;
+                                       kill(cd->pid, SIGTERM);
+                                       break;
+                               }
+
                                st = NULL;
                        }
                        else if(!strcmp(s, "CHART")) {
@@ -5740,8 +5765,10 @@ void *pluginsd_worker_thread(void *arg)
                                char *update_every_s = qstrsep(&p);
 
                                if(!type || !*type || !id || !*id) {
-                                       error("PLUGINSD: script %s is requesting a CHART, without a type.id", cd->fullfilename);
-                                       continue;
+                                       error("PLUGINSD: script %s is requesting a CHART, without a type.id. Disabling it.", cd->fullfilename);
+                                       cd->enabled = 0;
+                                       kill(cd->pid, SIGTERM);
+                                       break;
                                }
 
                                int priority = 1000;
@@ -5786,13 +5813,17 @@ void *pluginsd_worker_thread(void *arg)
                                char *hidden = qstrsep(&p);
 
                                if(!id || !*id) {
-                                       error("PLUGINSD: script %s is requesting a DIMENSION, without an id", cd->fullfilename);
-                                       continue;
+                                       error("PLUGINSD: script %s is requesting a DIMENSION, without an id. Disabling it.", cd->fullfilename);
+                                       cd->enabled = 0;
+                                       kill(cd->pid, SIGTERM);
+                                       break;
                                }
 
                                if(!st) {
-                                       error("PLUGINSD: script %s is requesting a DIMENSION, without a CHART", cd->fullfilename);
-                                       continue;
+                                       error("PLUGINSD: script %s is requesting a DIMENSION, without a CHART. Disabling it.", cd->fullfilename);
+                                       cd->enabled = 0;
+                                       kill(cd->pid, SIGTERM);
+                                       break;
                                }
 
                                long multiplier = 1;
@@ -5826,17 +5857,27 @@ void *pluginsd_worker_thread(void *arg)
                                char *pid_s = qstrsep(&p);
                                pid_t pid = atol(pid_s);
 
-                               if(pid != cd->pid)
-                                       error("plugin %s reports wrong pid %d (expected %d).", cd->filename, pid, cd->pid);
+                               if(pid != cd->pid) {
+                                       error("plugin %s reports wrong pid %d (expected %d). Disabling it.", cd->filename, pid, cd->pid);
+                                       cd->enabled = 0;
+                                       kill(cd->pid, SIGTERM);
+                                       break;
+                               }
 
                                debug(D_PLUGINSD, "PLUGINSD: %s is on pid %d", cd->id, cd->pid);
                        }
                        else if(!strcmp(s, "DISABLE")) {
                                error("PLUGINSD: script '%s' called DISABLE. Disabling it.", cd->fullfilename);
                                cd->enabled = 0;
+                               kill(cd->pid, SIGTERM);
+                               break;
+                       }
+                       else {
+                               error("PLUGINSD: script %s is sending command '%s' which is not known by netdata. Disabling it.", cd->fullfilename, s);
+                               cd->enabled = 0;
+                               kill(cd->pid, SIGTERM);
                                break;
                        }
-                       else error("PLUGINSD: script %s is sending command '%s' which is not known by netdata", cd->fullfilename, s);
                }
 
                // fgets() failed or loop broke
@@ -5845,6 +5886,7 @@ void *pluginsd_worker_thread(void *arg)
                if(!count && cd->enabled) {
                        error("PLUGINSD: script '%s' does not generate usefull output. Disabling it.", cd->fullfilename);
                        cd->enabled = 0;
+                       kill(cd->pid, SIGTERM);
                }
 
                if(cd->enabled) sleep(cd->update_every);
@@ -5863,7 +5905,7 @@ void *pluginsd_main(void *ptr)
        int scan_frequency = config_get_number("plugins", "check for new plugins every", 60);
        DIR *dir = NULL;
        struct dirent *file = NULL;
-       struct chartd *cd;
+       struct plugind *cd;
 
        if(scan_frequency < 1) scan_frequency = 1;
 
@@ -5907,7 +5949,7 @@ void *pluginsd_main(void *ptr)
                        // it is not running
                        // allocate a new one, or use the obsolete one
                        if(!cd) {
-                               cd = calloc(sizeof(struct chartd), 1);
+                               cd = calloc(sizeof(struct plugind), 1);
                                if(!cd) fatal("Cannot allocate memory for chart.d");
 
                                snprintf(cd->id, CONFIG_MAX_NAME, "plugin:%s", pluginname);
@@ -5952,7 +5994,7 @@ void kill_childs()
        if(tc_child_pid) kill(tc_child_pid, SIGTERM);
        tc_child_pid = 0;
 
-       struct chartd *cd;
+       struct plugind *cd;
        for(cd = pluginsd_root ; cd ; cd = cd->next)
                if(cd->pid && !cd->obsolete) {
                        kill(cd->pid, SIGTERM);
index fbea969cc76c5f618ef34400289261fa0c3efdd5..77849902257eeb9f6b656850409e23a58d293910 100755 (executable)
@@ -46,7 +46,7 @@ then
                echo "You are having an old config. Moved to netdata.conf.old. New config is in conf.d/"
        else
                # find if debug is enabled in the config
-               df=`grep -C 3 "\[debug\]" conf.d/netdata.conf | grep "flags" | tail -n 1 | cut -d '=' -f 2 | sed s"/ //g"`
+               df=`grep "debug flags = " conf.d/netdata.conf | tail -n 1 | cut -d '=' -f 2 | sed s"/ //g"`
                if [ -z "$df" -o "$df" = "0x00000000" -o $((df + 1 - 1)) -eq 0 ]
                then
                        NETDATA_CONFIG_DEBUG=0
@@ -64,10 +64,10 @@ echo "Compiling netdata (debug=$NETDATA_CONFIG_DEBUG)"
 if [ $NETDATA_CONFIG_DEBUG -eq 1 ]
 then
        ulimit -c unlimited
-       gcc -Wall -Wextra -ggdb -o netdata netdata.c -lpthread -lz || exit 1
+       make debug=1 || exit 1
        debug_opts="-df 0xfffffadf"
 else
-       gcc -Wall -O3 -o netdata netdata.c -lpthread -lz || exit 1
+       make || exit 1
        debug_opts="-df 0x00000000"
 fi