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