@cryptotaxi247 / netdata-1 / commits / 90692d5df

full apps monitor plugin writen in C

Costa Tsaousis (ktsaou) committed May 5, 2014 at 02:00 UTC 90692d5df839ad52535eeadf1daa7eeeac3116e1
3 files changed +732 -37
apps_plugin.c new
+653
@@ -0,0 +1,653 @@
1 +#include <stdio.h>
2 +#include <stdlib.h>
3 +#include <string.h>
4 +#include <time.h>
5 +#include <unistd.h>
6 +#include <sys/types.h>
7 +#include <sys/time.h>
8 +#include <sys/wait.h>
9 +
10 +#include <sys/resource.h>
11 +#include <sys/stat.h>
12 +
13 +#include <errno.h>
14 +#include <stdarg.h>
15 +#include <locale.h>
16 +#include <ctype.h>
17 +#include <fcntl.h>
18 +
19 +#include <malloc.h>
20 +#include <inttypes.h>
21 +#include <dirent.h>
22 +#include <arpa/inet.h>
23 +
24 +#define MAX_COMPARE_NAME 15
25 +
26 +
27 +#define add_childs 1ULL
28 +
29 +unsigned long long Hertz = 1;
30 +
31 +long pid_max = 32768;
32 +int debug = 0;
33 +
34 +struct wanted {
35 + char compare[MAX_COMPARE_NAME + 3];
36 + char id[FILENAME_MAX+1];
37 + char name[FILENAME_MAX+1];
38 +
39 + unsigned long long minflt;
40 + unsigned long long cminflt;
41 + unsigned long long majflt;
42 + unsigned long long cmajflt;
43 + unsigned long long utime;
44 + unsigned long long stime;
45 + unsigned long long cutime;
46 + unsigned long long cstime;
47 + unsigned long long num_threads;
48 + unsigned long long rss;
49 +
50 + int merge_count;
51 +
52 + struct wanted *target; // the one that will be reported to netdata
53 + struct wanted *next;
54 +} *wanted_root = NULL;
55 +
56 +int update_every = 1;
57 +
58 +struct wanted *add_wanted(const char *id, struct wanted *target)
59 +{
60 + struct wanted *w = calloc(sizeof(struct wanted), 1);
61 + if(!w) {
62 + fprintf(stderr, "Cannot allocate %ld bytes of memory\n", sizeof(struct wanted));
63 + return NULL;
64 + }
65 +
66 + strncpy(w->id, id, FILENAME_MAX);
67 + strncpy(w->name, id, FILENAME_MAX);
68 + snprintf(w->compare, MAX_COMPARE_NAME+1, "(%.*s)", MAX_COMPARE_NAME, id);
69 + w->target = target;
70 +
71 + w->next = wanted_root;
72 + wanted_root = w;
73 +
74 + if(debug) fprintf(stderr, "Adding hook for process '%s', compare '%s' on target '%s'\n", w->id, w->compare, w->target?w->target->id:"");
75 +
76 + return w;
77 +}
78 +
79 +void parse_args(int argc, char **argv)
80 +{
81 + int i = 1;
82 +
83 + debug = 0;
84 + if(strcmp(argv[i], "debug") == 0) {
85 + debug = 1;
86 + i++;
87 + }
88 +
89 + update_every = atoi(argv[i++]);
90 + if(update_every == 0) {
91 + i = 1;
92 + update_every = 1;
93 + }
94 +
95 + for(; i < argc ; i++) {
96 + struct wanted *w = NULL;
97 + char *s = argv[i];
98 + char *t;
99 +
100 + while((t = strsep(&s, " "))) {
101 + if(w && strcmp(t, "as") == 0 && s && *s) {
102 + strncpy(w->name, s, FILENAME_MAX);
103 + if(debug) fprintf(stderr, "Setting dimension name to '%s' on target '%s'\n", w->name, w->id);
104 + break;
105 + }
106 +
107 + struct wanted *n = add_wanted(t, w);
108 + if(!w) w = n;
109 + }
110 + }
111 +}
112 +
113 +// see: man proc
114 +struct pid_stat {
115 + int32_t pid;
116 + char comm[FILENAME_MAX+1];
117 + char state;
118 + int32_t ppid;
119 + int32_t pgrp;
120 + int32_t session;
121 + int32_t tty_nr;
122 + int32_t tpgid;
123 + uint64_t flags;
124 + unsigned long long minflt;
125 + unsigned long long cminflt;
126 + unsigned long long majflt;
127 + unsigned long long cmajflt;
128 + unsigned long long utime;
129 + unsigned long long stime;
130 + unsigned long long cutime;
131 + unsigned long long cstime;
132 + int64_t priority;
133 + int64_t nice;
134 + int32_t num_threads;
135 + int64_t itrealvalue;
136 + unsigned long long starttime;
137 + unsigned long long vsize;
138 + unsigned long long rss;
139 + unsigned long long rsslim;
140 + unsigned long long starcode;
141 + unsigned long long endcode;
142 + unsigned long long startstack;
143 + unsigned long long kstkesp;
144 + unsigned long long kstkeip;
145 + uint64_t signal;
146 + uint64_t blocked;
147 + uint64_t sigignore;
148 + uint64_t sigcatch;
149 + uint64_t wchan;
150 + uint64_t nswap;
151 + uint64_t cnswap;
152 + int32_t exit_signal;
153 + int32_t processor;
154 + uint32_t rt_priority;
155 + uint32_t policy;
156 + unsigned long long delayacct_blkio_ticks;
157 + uint64_t guest_time;
158 + int64_t cguest_time;
159 +
160 + int childs; // number of processes directly referencing this
161 + int updated;
162 + int merged;
163 + int new_entry;
164 + int merge_count;
165 + struct wanted *target;
166 + struct pid_stat *parent;
167 + struct pid_stat *prev;
168 + struct pid_stat *next;
169 +} *root = NULL;
170 +
171 +struct pid_stat **all_pids;
172 +
173 +#define PID_STAT_LINE_MAX 4096
174 +
175 +struct pid_stat *get_entry(pid_t pid)
176 +{
177 + if(all_pids[pid]) {
178 + all_pids[pid]->new_entry = 0;
179 + return all_pids[pid];
180 + }
181 +
182 + all_pids[pid] = calloc(sizeof(struct pid_stat), 1);
183 + if(!all_pids[pid]) {
184 + fprintf(stderr, "Cannot allocate %lu bytes of memory", sizeof(struct pid_stat));
185 + return NULL;
186 + }
187 +
188 + if(root) root->prev = all_pids[pid];
189 + all_pids[pid]->next = root;
190 + root = all_pids[pid];
191 +
192 + all_pids[pid]->new_entry = 1;
193 +
194 + return all_pids[pid];
195 +}
196 +
197 +void del_entry(pid_t pid)
198 +{
199 + if(!all_pids[pid]) return;
200 +
201 + if(root == all_pids[pid]) root = all_pids[pid]->next;
202 + if(all_pids[pid]->next) all_pids[pid]->next->prev = all_pids[pid]->prev;
203 + if(all_pids[pid]->prev) all_pids[pid]->prev->next = all_pids[pid]->next;
204 +
205 + free(all_pids[pid]);
206 + all_pids[pid] = NULL;
207 +}
208 +
209 +int update_from_proc(void)
210 +{
211 + char buffer[PID_STAT_LINE_MAX + 1];
212 + char filename[FILENAME_MAX+1];
213 + DIR *dir = opendir("/proc");
214 + if(!dir) return 0;
215 +
216 + struct dirent *file = NULL;
217 + struct pid_stat *p = NULL;
218 +
219 + // mark them all as un-updated
220 + for(p = root; p ; p = p->next) {
221 + p->parent = NULL;
222 + p->updated = 0;
223 + p->childs = 0;
224 + p->merged = 0;
225 + p->new_entry = 0;
226 + p->merge_count = 0;
227 + }
228 +
229 + while((file = readdir(dir))) {
230 + char *endptr = file->d_name;
231 + pid_t pid = strtoul(file->d_name, &endptr, 10);
232 + if(pid <= 0 || pid > pid_max || endptr == file->d_name || *endptr != '\0') continue;
233 +
234 + snprintf(filename, FILENAME_MAX, "/proc/%s/stat", file->d_name);
235 +
236 + int fd = open(filename, O_RDONLY);
237 + if(fd == -1) {
238 + if(errno != ENOENT) fprintf(stderr, "Cannot open file '%s' for reading (%s).\n", filename, strerror(errno));
239 + continue;
240 + }
241 +
242 + int bytes = read(fd, buffer, PID_STAT_LINE_MAX);
243 + if(bytes == -1) {
244 + fprintf(stderr, "Cannot read from file '%s' (%s).\n", filename, strerror(errno));
245 + close(fd);
246 + continue;
247 + }
248 +
249 + close(fd);
250 + if(bytes < 100) continue;
251 +
252 + p = get_entry(pid);
253 + if(!p) continue;
254 +
255 + int parsed = sscanf(buffer,
256 + "%d %s %c" // pid, comm, state
257 + " %d %d %d %d %d" // ppid, pgrp, session, tty_nr, tpgid
258 + " %lu %llu %llu %llu %llu" // flags, minflt, cminflt, majflt, cmajflt
259 + " %llu %llu %llu %llu" // utime, stime, cutime, cstime
260 + " %ld %ld" // priority, nice
261 + " %d" // num_threads
262 + " %ld" // itrealvalue
263 + " %llu" // starttime
264 + " %llu" // vsize
265 + " %llu" // rss
266 + " %llu %llu %llu %llu %llu %llu" // rsslim, starcode, endcode, startstack, kstkesp, kstkeip
267 + " %lu %lu %lu %lu" // signal, blocked, sigignore, sigcatch
268 + " %lu %lu %lu" // wchan, nswap, cnswap
269 + " %d %d" // exit_signal, processor
270 + " %u %u" // rt_priority, policy
271 + " %llu %lu %ld"
272 + , &p->pid, p->comm, &p->state
273 + , &p->ppid, &p->pgrp, &p->session, &p->tty_nr, &p->tpgid
274 + , &p->flags, &p->minflt, &p->cminflt, &p->majflt, &p->cmajflt
275 + , &p->utime, &p->stime, &p->cutime, &p->cstime
276 + , &p->priority, &p->nice
277 + , &p->num_threads
278 + , &p->itrealvalue
279 + , &p->starttime
280 + , &p->vsize
281 + , &p->rss
282 + , &p->rsslim, &p->starcode, &p->endcode, &p->startstack, &p->kstkesp, &p->kstkeip
283 + , &p->signal, &p->blocked, &p->sigignore, &p->sigcatch
284 + , &p->wchan, &p->nswap, &p->cnswap
285 + , &p->exit_signal, &p->processor
286 + , &p->rt_priority, &p->policy
287 + , &p->delayacct_blkio_ticks, &p->guest_time, &p->cguest_time
288 + );
289 +
290 + if(parsed < 39) fprintf(stderr, "file %s gave %d results (expected 44)\n", filename, parsed);
291 +
292 + // check if it is wanted
293 + // we do this only once, the first time this pid is loaded
294 + if(p->new_entry) {
295 + if(debug) fprintf(stderr, "\tJust added %s\n", p->comm);
296 +
297 + struct wanted *w;
298 + for(w = wanted_root; w ; w = w->next) {
299 + // if(debug) fprintf(stderr, "\t\tcomparing '%s' with '%s'\n", w->compare, p->comm);
300 +
301 + if(strcmp(w->compare, p->comm) == 0) {
302 + if(w->target) p->target = w->target;
303 + else p->target = w;
304 +
305 + if(debug) fprintf(stderr, "\t\t%s linked to target %s\n", p->comm, p->target->name);
306 + }
307 + }
308 + }
309 +
310 + // just a few checks
311 + if(p->ppid < 1 || p->ppid > pid_max) p->ppid = 1;
312 +
313 + // mark it as updated
314 + p->updated = 1;
315 + }
316 + closedir(dir);
317 +
318 + // cleanup all un-updated
319 + int c;
320 + for(p = root, c = 0; p ; c++) {
321 + if(!p->updated) {
322 + pid_t r = p->pid;
323 + p = p->next;
324 + del_entry(r);
325 + }
326 + else p = p->next;
327 + }
328 +
329 + if(debug) fprintf(stderr, "There are %d processes active\n", c);
330 +
331 + return 1;
332 +}
333 +
334 +void merge_processes(void)
335 +{
336 + struct pid_stat *p = NULL;
337 +
338 + // link all parents and update childs
339 + for(p = root; p ; p = p->next) {
340 + if(p->ppid > 1 && p->ppid <= pid_max && all_pids[p->ppid]) {
341 + if(debug) fprintf(stderr, "\tParent of %d %s is %d %s\n", p->pid, p->comm, p->ppid, all_pids[p->ppid]->comm);
342 +
343 + p->parent = all_pids[p->ppid];
344 + if(!p->parent) {
345 + if(debug) fprintf(stderr, "\t\tpid %d %s states parent %d, but the later does not exist.\n", p->pid, p->comm, p->ppid);
346 + continue;
347 + }
348 +
349 + p->parent->childs++;
350 + }
351 + }
352 +
353 + // find all the procs with 0 childs and update their parents
354 + // continue, until nothing more can be done.
355 + int found = 1;
356 + for( ; found ; ) {
357 + found = 0;
358 + for(p = root; p ; p = p->next) {
359 + if(!p->childs && !p->merged && p->parent && p->parent->childs) {
360 + 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);
361 +
362 + p->parent->minflt += p->minflt;
363 + p->parent->majflt += p->majflt;
364 + p->parent->utime += p->utime;
365 + p->parent->stime += p->stime;
366 +
367 + p->parent->cminflt += p->cminflt;
368 + p->parent->cmajflt += p->cmajflt;
369 + p->parent->cutime += p->cutime;
370 + p->parent->cstime += p->cstime;
371 +
372 + p->parent->num_threads += p->num_threads;
373 + p->parent->rss += p->rss;
374 +
375 + p->parent->childs--;
376 + p->merged = 1;
377 +
378 + p->parent->merge_count++;
379 +
380 + // the parent inherits the child's target, if it does not have a target itself
381 + if(p->target && !p->parent->target) {
382 + p->parent->target = p->target;
383 + 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);
384 + }
385 +
386 + found++;
387 + }
388 + }
389 + if(debug) fprintf(stderr, "Merged %d processes\n", found);
390 + }
391 +
392 + if(debug) {
393 + fprintf(stderr, "Root level processes: \n");
394 + for(p = root, found = 0; p ; p = p->next) {
395 + if(!p->childs && !p->merged && !p->parent) {
396 + fprintf(stderr, "\t\t%s %d on target %s\n", p->comm, p->pid, p->target?p->target->name:"-");
397 + }
398 + }
399 + }
400 +
401 + // zero the targets
402 + struct wanted *w;
403 + for (w = wanted_root; w ; w = w->next) {
404 + w->minflt = 0;
405 + w->majflt = 0;
406 + w->utime = 0;
407 + w->stime = 0;
408 + w->cminflt = 0;
409 + w->cmajflt = 0;
410 + w->cutime = 0;
411 + w->cstime = 0;
412 + w->num_threads = 0;
413 + w->rss = 0;
414 + w->merge_count = 0;
415 + }
416 +
417 + // concentrate everything on the targets
418 + for(p = root; p ; p = p->next) {
419 + if(!p->childs && !p->merged && !p->parent && p->target) {
420 + p->target->minflt += p->minflt;
421 + p->target->majflt += p->majflt;
422 + p->target->utime += p->utime;
423 + p->target->stime += p->stime;
424 + p->target->cminflt += p->cminflt;
425 + p->target->cmajflt += p->cmajflt;
426 + p->target->cutime += p->cutime;
427 + p->target->cstime += p->cstime;
428 + p->target->num_threads += p->num_threads;
429 + p->target->rss += p->rss;
430 +
431 + p->target->merge_count++;
432 + if(debug) fprintf(stderr, "\tAgregating %s pid %d on %s (count: %d)\n", p->comm, p->pid, p->target->name, p->target->merge_count);
433 + }
434 + }
435 +}
436 +
437 +
438 +void show_dimensions(void)
439 +{
440 + struct wanted *w;
441 +
442 + fprintf(stdout, "BEGIN apps.cpu\n");
443 + for (w = wanted_root; w ; w = w->next) {
444 + if(w->target) continue;
445 +
446 + fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + w->stime + (w->cutime * add_childs) + (w->cstime * add_childs));
447 + }
448 + fprintf(stdout, "END\n");
449 +
450 + fprintf(stdout, "BEGIN apps.cpu_user\n");
451 + for (w = wanted_root; w ; w = w->next) {
452 + if(w->target) continue;
453 +
454 + fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + (w->cutime * add_childs));
455 + }
456 + fprintf(stdout, "END\n");
457 +
458 + fprintf(stdout, "BEGIN apps.cpu_system\n");
459 + for (w = wanted_root; w ; w = w->next) {
460 + if(w->target) continue;
461 +
462 + fprintf(stdout, "SET %s = %llu\n", w->name, w->stime + (w->cstime * add_childs));
463 + }
464 + fprintf(stdout, "END\n");
465 +
466 + fprintf(stdout, "BEGIN apps.threads\n");
467 + for (w = wanted_root; w ; w = w->next) {
468 + if(w->target) continue;
469 +
470 + fprintf(stdout, "SET %s = %llu\n", w->name, w->num_threads);
471 + }
472 + fprintf(stdout, "END\n");
473 +
474 + fprintf(stdout, "BEGIN apps.rss\n");
475 + for (w = wanted_root; w ; w = w->next) {
476 + if(w->target) continue;
477 +
478 + fprintf(stdout, "SET %s = %llu\n", w->name, (unsigned long long)w->rss);
479 + }
480 + fprintf(stdout, "END\n");
481 +
482 + fprintf(stdout, "BEGIN apps.minor_faults\n");
483 + for (w = wanted_root; w ; w = w->next) {
484 + if(w->target) continue;
485 +
486 + fprintf(stdout, "SET %s = %llu\n", w->name, w->minflt + (w->cminflt * add_childs));
487 + }
488 + fprintf(stdout, "END\n");
489 +
490 + fprintf(stdout, "BEGIN apps.major_faults\n");
491 + for (w = wanted_root; w ; w = w->next) {
492 + if(w->target) continue;
493 +
494 + fprintf(stdout, "SET %s = %llu\n", w->name, w->majflt + (w->cmajflt * add_childs));
495 + }
496 + fprintf(stdout, "END\n");
497 +
498 + fflush(stdout);
499 +}
500 +
501 +void show_charts(void)
502 +{
503 + struct wanted *w;
504 +
505 + fprintf(stdout, "CHART apps.cpu '' 'Applications CPU Time' 'cpu time %%' apps apps stacked 20001 %d\n", update_every);
506 + for (w = wanted_root; w ; w = w->next) {
507 + if(w->target) continue;
508 +
509 + fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz);
510 + }
511 +
512 + fprintf(stdout, "CHART apps.rss '' 'Applications Memory' 'MB' apps apps stacked 20002 %d\n", update_every);
513 + for (w = wanted_root; w ; w = w->next) {
514 + if(w->target) continue;
515 +
516 + fprintf(stdout, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L);
517 + }
518 +
519 + fprintf(stdout, "CHART apps.threads '' 'Applications Threads' 'threads' apps apps stacked 20005 %d\n", update_every);
520 + for (w = wanted_root; w ; w = w->next) {
521 + if(w->target) continue;
522 +
523 + fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
524 + }
525 +
526 + fprintf(stdout, "CHART apps.cpu_user '' 'Applications CPU User Time' 'cpu time %%' apps none stacked 20020 %d\n", update_every);
527 + for (w = wanted_root; w ; w = w->next) {
528 + if(w->target) continue;
529 +
530 + fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz);
531 + }
532 +
533 + fprintf(stdout, "CHART apps.cpu_system '' 'Applications CPU System Time' 'cpu time %%' apps none stacked 20021 %d\n", update_every);
534 + for (w = wanted_root; w ; w = w->next) {
535 + if(w->target) continue;
536 +
537 + fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz);
538 + }
539 +
540 + fprintf(stdout, "CHART apps.major_faults '' 'Applications Major Page Faults' 'page faults/s' apps apps stacked 20010 %d\n", update_every);
541 + for (w = wanted_root; w ; w = w->next) {
542 + if(w->target) continue;
543 +
544 + fprintf(stdout, "DIMENSION %s '' incremental 1 1\n", w->name);
545 + }
546 +
547 + fprintf(stdout, "CHART apps.minor_faults '' 'Applications Minor Page Faults' 'page faults/s' apps apps stacked 20011 %d\n", update_every);
548 + for (w = wanted_root; w ; w = w->next) {
549 + if(w->target) continue;
550 +
551 + fprintf(stdout, "DIMENSION %s '' incremental 1 1\n", w->name);
552 + }
553 +
554 + fflush(stdout);
555 +}
556 +
557 +unsigned long long get_hertz(void)
558 +{
559 + unsigned long long hz = 1;
560 +
561 +#ifdef _SC_CLK_TCK
562 + if((hz = sysconf(_SC_CLK_TCK)) > 0) {
563 + return hz;
564 + }
565 +#endif
566 +
567 +#ifdef HZ
568 + hz = (unsigned long long)HZ; /* <asm/param.h> */
569 +#else
570 + /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */
571 + hz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;
572 +#endif
573 +
574 + fprintf(stderr, "Unknown HZ value. Assuming %llu", hz);
575 + return hz;
576 +}
577 +
578 +
579 +long get_pid_max(void)
580 +{
581 + return 32768L;
582 +}
583 +
584 +unsigned long long usecdiff(struct timeval *now, struct timeval *last) {
585 + return ((((now->tv_sec * 1000000ULL) + now->tv_usec) - ((last->tv_sec * 1000000ULL) + last->tv_usec)));
586 +}
587 +
588 +void mysleep(unsigned long long susec)
589 +{
590 + while(susec) {
591 + struct timeval t1, t2;
592 +
593 + gettimeofday(&t1, NULL);
594 +
595 + if(debug) fprintf(stderr, "Sleeping for %llu microseconds\n", susec);
596 + usleep(susec);
597 +
598 + gettimeofday(&t2, NULL);
599 +
600 + unsigned long long diff = usecdiff(&t2, &t1);
601 +
602 + if(diff < susec) susec -= diff;
603 + else susec = 0;
604 + }
605 +}
606 +
607 +int main(int argc, char **argv)
608 +{
609 + Hertz = get_hertz();
610 + pid_max = get_pid_max();
611 +
612 + parse_args(argc, argv);
613 +
614 + all_pids = calloc(sizeof(struct pid_stat *), pid_max);
615 + if(!all_pids) {
616 + fprintf(stderr, "Cannot allocate %lu bytes of memory.\n", sizeof(struct pid_stat *) * pid_max);
617 + printf("DISABLE\n");
618 + exit(1);
619 + }
620 +
621 + show_charts();
622 +
623 + unsigned long long counter = 1;
624 + unsigned long long usec = 0, susec = 0;
625 + struct timeval last, now;
626 + gettimeofday(&last, NULL);
627 +
628 + for(;1; counter++) {
629 + if(!update_from_proc()) {
630 + fprintf(stderr, "Cannot allocate %lu bytes of memory.\n", sizeof(struct pid_stat *) * pid_max);
631 + printf("DISABLE\n");
632 + exit(1);
633 + }
634 +
635 + merge_processes();
636 + show_dimensions();
637 +
638 + if(debug) fprintf(stderr, "Done Loop No %llu\n", counter);
639 + // if(counter == 1000) exit(0);
640 +
641 + gettimeofday(&now, NULL);
642 +
643 + usec = usecdiff(&now, &last) - susec;
644 + if(debug) fprintf(stderr, "last loop took %llu usec (worked for %llu, sleeped for %llu).\n", usec + susec, usec, susec);
645 +
646 + if(usec < (update_every * 1000000ULL)) susec = (update_every * 1000000ULL) - usec;
647 + else susec = 0;
648 +
649 + mysleep(susec);
650 +
651 + bcopy(&now, &last, sizeof(struct timeval));
652 + }
653 +}
netdata.c
+76 -34
@@ -313,6 +313,9 @@ FILE *mypopen(const char *command, pid_t *pidptr)
313 for(i = sysconf(_SC_OPEN_MAX); i > 0; i--)
314 if(i != STDIN_FILENO && i != STDERR_FILENO && i != pipefd[PIPE_WRITE]) close(i);
315
316 + // ignore all signals
317 + for (i = 1 ; i < 65 ;i++) if(i != SIGSEGV) signal(i, SIG_DFL);
318 +
319 // move the pipe to stdout
320 if(pipefd[PIPE_WRITE] != STDOUT_FILENO) {
321 dup2(pipefd[PIPE_WRITE], STDOUT_FILENO);
@@ -1161,7 +1164,7 @@ int rrd_stats_dimension_set(RRD_STATS *st, char *id, collected_number value)
1164 return 0;
1165 }
1166
1164 -unsigned long long rrd_stats_next(RRD_STATS *st)
1167 +void rrd_stats_next(RRD_STATS *st)
1168 {
1169 pthread_mutex_lock(&st->mutex);
1170
@@ -1180,10 +1183,10 @@ unsigned long long rrd_stats_next(RRD_STATS *st)
1183
1184 pthread_mutex_unlock(&st->mutex);
1185
1183 - return st->usec_since_last_update;
1186 + return;
1187 }
1188
1186 -void rrd_stats_done(RRD_STATS *st)
1189 +unsigned long long rrd_stats_done(RRD_STATS *st)
1190 {
1191 pthread_mutex_lock(&st->mutex);
1192
@@ -1218,7 +1221,7 @@ void rrd_stats_done(RRD_STATS *st)
1221 st->enabled = 0;
1222 pthread_mutex_unlock(&st->mutex);
1223 // rrd_stats_free(st);
1221 - return;
1224 + return st->update_every * 1000000ULL;
1225 }
1226
1227 // calculate totals and count the dimensions
@@ -1372,12 +1375,15 @@ void rrd_stats_done(RRD_STATS *st)
1375 // store the time difference to the last entry
1376 st->timediff[st->current_entry] = st->usec_since_last_update;
1377 }
1378 + else st->usec_since_last_update = st->update_every * 1000000ULL;
1379
1380 st->last_updated.tv_sec = now.tv_sec;
1381 st->last_updated.tv_usec = now.tv_usec;
1382 st->counter++;
1383
1384 pthread_mutex_unlock(&st->mutex);
1385 +
1386 + return st->usec_since_last_update;
1387 }
1388
1389
@@ -5309,8 +5315,7 @@ void tc_device_commit(struct tc_device *d)
5315 }
5316 }
5317 else {
5312 - unsigned long long usec = rrd_stats_next(st);
5313 - debug(D_TC_LOOP, "TC: Committing TC device '%s' after %llu usec", d->name, usec);
5318 + rrd_stats_next(st);
5319
5320 if(strcmp(d->id, d->name) != 0) {
5321 // update the device name with the new one
@@ -5599,7 +5604,7 @@ void *cpuidlejitter_main(void *ptr)
5604 #define PLUGINSD_CMD_MAX (FILENAME_MAX*2)
5605 #define PLUGINSD_LINE_MAX 1024
5606
5602 -struct chartd {
5607 +struct plugind {
5608 char id[CONFIG_MAX_NAME+1]; // config node id
5609
5610 char filename[FILENAME_MAX+1]; // just the filename
@@ -5613,7 +5618,7 @@ struct chartd {
5618 int obsolete;
5619 int enabled;
5620
5616 - struct chartd *next;
5621 + struct plugind *next;
5622 } *pluginsd_root = NULL;
5623
5624 // like strsep() but:
@@ -5656,7 +5661,7 @@ char *qstrsep(char **ptr)
5661
5662 void *pluginsd_worker_thread(void *arg)
5663 {
5659 - struct chartd *cd = (struct chartd *)arg;
5664 + struct plugind *cd = (struct plugind *)arg;
5665 char line[PLUGINSD_LINE_MAX + 1];
5666
5667 while(1) {
@@ -5678,18 +5683,24 @@ void *pluginsd_worker_thread(void *arg)
5683
5684 if(!s || !*s) continue;
5685 else if(!strcmp(s, "SET")) {
5686 + char *t;
5687 + while((t = strchr(p, '='))) *t = ' ';
5688 +
5689 char *dimension = qstrsep(&p);
5682 - char *equal = qstrsep(&p);
5690 char *value = qstrsep(&p);
5691
5685 - if(!dimension || !equal || *equal != '=' || !value) {
5686 - 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:"");
5687 - continue;
5692 + if(!dimension || !*dimension || !value) {
5693 + 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:"");
5694 + cd->enabled = 0;
5695 + kill(cd->pid, SIGTERM);
5696 + break;
5697 }
5698
5699 if(!st) {
5691 - error("PLUGINSD: script %s is requesting a SET, without a BEGIN", cd->fullfilename);
5692 - continue;
5700 + error("PLUGINSD: script %s is requesting a SET on dimension %s with value %s, without a BEGIN. Disabling it.", cd->fullfilename, dimension, value);
5701 + cd->enabled = 0;
5702 + kill(cd->pid, SIGTERM);
5703 + break;
5704 }
5705
5706 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)
5711 else if(!strcmp(s, "BEGIN")) {
5712 char *id = qstrsep(&p);
5713 if(!id) {
5703 - error("PLUGINSD: script %s is requesting a BEGIN without a chart id", cd->fullfilename);
5704 - continue;
5714 + error("PLUGINSD: script %s is requesting a BEGIN without a chart id. Disabling it.", cd->fullfilename);
5715 + cd->enabled = 0;
5716 + kill(cd->pid, SIGTERM);
5717 + break;
5718 }
5719
5720 st = rrd_stats_find(id);
5721 if(!st) {
5709 - error("PLUGINSD: script %s is requesting a BEGIN on chart '%s', which does not exist", cd->fullfilename, id);
5710 - continue;
5722 + error("PLUGINSD: script %s is requesting a BEGIN on chart '%s', which does not exist. Disabling it.", cd->fullfilename, id);
5723 + cd->enabled = 0;
5724 + kill(cd->pid, SIGTERM);
5725 + break;
5726 }
5727 if(st->counter) rrd_stats_next(st);
5728 }
5729 else if(!strcmp(s, "END")) {
5730 if(!st) {
5716 - error("PLUGINSD: script %s is requesting an END, without a BEGIN", cd->fullfilename);
5717 - continue;
5731 + error("PLUGINSD: script %s is requesting an END, without a BEGIN. Disabling it.", cd->fullfilename);
5732 + cd->enabled = 0;
5733 + kill(cd->pid, SIGTERM);
5734 + break;
5735 }
5736
5737 if(st->debug) debug(D_PLUGINSD, "PLUGINSD: script %s is requesting a END on chart %s", cd->fullfilename, st->id);
5721 - rrd_stats_done(st);
5738 +
5739 + unsigned long long usec_since_last_update = rrd_stats_done(st);
5740 + if(usec_since_last_update < (cd->update_every * 1000000ULL / 2)) {
5741 + 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);
5742 + cd->enabled = 0;
5743 + kill(cd->pid, SIGTERM);
5744 + break;
5745 + }
5746 +
5747 st = NULL;
5748 }
5749 else if(!strcmp(s, "CHART")) {
@@ -5740,8 +5765,10 @@ void *pluginsd_worker_thread(void *arg)
5765 char *update_every_s = qstrsep(&p);
5766
5767 if(!type || !*type || !id || !*id) {
5743 - error("PLUGINSD: script %s is requesting a CHART, without a type.id", cd->fullfilename);
5744 - continue;
5768 + error("PLUGINSD: script %s is requesting a CHART, without a type.id. Disabling it.", cd->fullfilename);
5769 + cd->enabled = 0;
5770 + kill(cd->pid, SIGTERM);
5771 + break;
5772 }
5773
5774 int priority = 1000;
@@ -5786,13 +5813,17 @@ void *pluginsd_worker_thread(void *arg)
5813 char *hidden = qstrsep(&p);
5814
5815 if(!id || !*id) {
5789 - error("PLUGINSD: script %s is requesting a DIMENSION, without an id", cd->fullfilename);
5790 - continue;
5816 + error("PLUGINSD: script %s is requesting a DIMENSION, without an id. Disabling it.", cd->fullfilename);
5817 + cd->enabled = 0;
5818 + kill(cd->pid, SIGTERM);
5819 + break;
5820 }
5821
5822 if(!st) {
5794 - error("PLUGINSD: script %s is requesting a DIMENSION, without a CHART", cd->fullfilename);
5795 - continue;
5823 + error("PLUGINSD: script %s is requesting a DIMENSION, without a CHART. Disabling it.", cd->fullfilename);
5824 + cd->enabled = 0;
5825 + kill(cd->pid, SIGTERM);
5826 + break;
5827 }
5828
5829 long multiplier = 1;
@@ -5826,17 +5857,27 @@ void *pluginsd_worker_thread(void *arg)
5857 char *pid_s = qstrsep(&p);
5858 pid_t pid = atol(pid_s);
5859
5829 - if(pid != cd->pid)
5830 - error("plugin %s reports wrong pid %d (expected %d).", cd->filename, pid, cd->pid);
5860 + if(pid != cd->pid) {
5861 + error("plugin %s reports wrong pid %d (expected %d). Disabling it.", cd->filename, pid, cd->pid);
5862 + cd->enabled = 0;
5863 + kill(cd->pid, SIGTERM);
5864 + break;
5865 + }
5866
5867 debug(D_PLUGINSD, "PLUGINSD: %s is on pid %d", cd->id, cd->pid);
5868 }
5869 else if(!strcmp(s, "DISABLE")) {
5870 error("PLUGINSD: script '%s' called DISABLE. Disabling it.", cd->fullfilename);
5871 cd->enabled = 0;
5872 + kill(cd->pid, SIGTERM);
5873 + break;
5874 + }
5875 + else {
5876 + error("PLUGINSD: script %s is sending command '%s' which is not known by netdata. Disabling it.", cd->fullfilename, s);
5877 + cd->enabled = 0;
5878 + kill(cd->pid, SIGTERM);
5879 break;
5880 }
5839 - else error("PLUGINSD: script %s is sending command '%s' which is not known by netdata", cd->fullfilename, s);
5881 }
5882
5883 // fgets() failed or loop broke
@@ -5845,6 +5886,7 @@ void *pluginsd_worker_thread(void *arg)
5886 if(!count && cd->enabled) {
5887 error("PLUGINSD: script '%s' does not generate usefull output. Disabling it.", cd->fullfilename);
5888 cd->enabled = 0;
5889 + kill(cd->pid, SIGTERM);
5890 }
5891
5892 if(cd->enabled) sleep(cd->update_every);
@@ -5863,7 +5905,7 @@ void *pluginsd_main(void *ptr)
5905 int scan_frequency = config_get_number("plugins", "check for new plugins every", 60);
5906 DIR *dir = NULL;
5907 struct dirent *file = NULL;
5866 - struct chartd *cd;
5908 + struct plugind *cd;
5909
5910 if(scan_frequency < 1) scan_frequency = 1;
5911
@@ -5907,7 +5949,7 @@ void *pluginsd_main(void *ptr)
5949 // it is not running
5950 // allocate a new one, or use the obsolete one
5951 if(!cd) {
5910 - cd = calloc(sizeof(struct chartd), 1);
5952 + cd = calloc(sizeof(struct plugind), 1);
5953 if(!cd) fatal("Cannot allocate memory for chart.d");
5954
5955 snprintf(cd->id, CONFIG_MAX_NAME, "plugin:%s", pluginname);
@@ -5952,7 +5994,7 @@ void kill_childs()
5994 if(tc_child_pid) kill(tc_child_pid, SIGTERM);
5995 tc_child_pid = 0;
5996
5955 - struct chartd *cd;
5997 + struct plugind *cd;
5998 for(cd = pluginsd_root ; cd ; cd = cd->next)
5999 if(cd->pid && !cd->obsolete) {
6000 kill(cd->pid, SIGTERM);
netdata.start
+3 -3
@@ -46,7 +46,7 @@ then
46 echo "You are having an old config. Moved to netdata.conf.old. New config is in conf.d/"
47 else
48 # find if debug is enabled in the config
49 - df=`grep -C 3 "\[debug\]" conf.d/netdata.conf | grep "flags" | tail -n 1 | cut -d '=' -f 2 | sed s"/ //g"`
49 + df=`grep "debug flags = " conf.d/netdata.conf | tail -n 1 | cut -d '=' -f 2 | sed s"/ //g"`
50 if [ -z "$df" -o "$df" = "0x00000000" -o $((df + 1 - 1)) -eq 0 ]
51 then
52 NETDATA_CONFIG_DEBUG=0
@@ -64,10 +64,10 @@ echo "Compiling netdata (debug=$NETDATA_CONFIG_DEBUG)"
64 if [ $NETDATA_CONFIG_DEBUG -eq 1 ]
65 then
66 ulimit -c unlimited
67 - gcc -Wall -Wextra -ggdb -o netdata netdata.c -lpthread -lz || exit 1
67 + make debug=1 || exit 1
68 debug_opts="-df 0xfffffadf"
69 else
70 - gcc -Wall -O3 -o netdata netdata.c -lpthread -lz || exit 1
70 + make || exit 1
71 debug_opts="-df 0x00000000"
72 fi
73