]> arthur.barton.de Git - netdata.git/blob - src/plugins.d/apps_plugin.c
last_pos was never set
[netdata.git] / src / plugins.d / apps_plugin.c
1 #define __STDC_FORMAT_MACROS
2 #include <inttypes.h>
3
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <time.h>
8 #include <unistd.h>
9 #include <sys/types.h>
10 #include <sys/time.h>
11 #include <sys/wait.h>
12
13 #include <sys/resource.h>
14 #include <sys/stat.h>
15
16 #include <errno.h>
17 #include <stdarg.h>
18 #include <locale.h>
19 #include <ctype.h>
20 #include <fcntl.h>
21
22 #include <malloc.h>
23 #include <inttypes.h>
24 #include <dirent.h>
25 #include <arpa/inet.h>
26
27 #include "../common.h"
28 #include "../log.h"
29 #include "../avl.h"
30 #include "../procfile.h"
31
32 #define MAX_COMPARE_NAME 15
33 #define MAX_NAME 100
34
35 unsigned long long Hertz = 1;
36
37 long processors = 1;
38 long pid_max = 32768;
39 int debug = 0;
40
41 int update_every = 1;
42 unsigned long long file_counter = 0;
43
44 #define PROC_BUFFER 4096
45
46
47 // ----------------------------------------------------------------------------
48 // helper functions
49
50 long get_processors(void) {
51         int processors = 0;
52
53         procfile *ff = procfile_open("/proc/stat", O_RDONLY);
54         if(!ff) return 1;
55
56         ff = procfile_readall(ff);
57         if(!ff) {
58                 procfile_close(ff);
59                 return 1;
60         }
61
62         unsigned int i;
63         for(i = 0; i < procfile_lines(ff); i++) {
64                 if(!procfile_linewords(ff, i)) continue;
65
66                 if(strncmp(procfile_lineword(ff, i, 0), "cpu", 3) == 0) processors++;
67         }
68         processors--;
69         if(processors < 1) processors = 1;
70
71         procfile_close(ff);
72         return processors;
73 }
74
75 long get_pid_max(void) {
76         long mpid = 32768;
77
78         procfile *ff = procfile_open("/proc/sys/kernel/pid_max", O_RDONLY);
79         if(!ff) return mpid;
80
81         ff = procfile_readall(ff);
82         if(!ff) {
83                 procfile_close(ff);
84                 return mpid;
85         }
86
87         mpid = atol(procfile_lineword(ff, 0, 0));
88         if(mpid) mpid = 32768;
89
90         procfile_close(ff);
91         return mpid;
92 }
93
94 unsigned long long get_hertz(void)
95 {
96         unsigned long long hz = 1;
97
98 #ifdef _SC_CLK_TCK
99         if((hz = sysconf(_SC_CLK_TCK)) > 0) {
100                 return hz;
101         }
102 #endif
103
104 #ifdef HZ
105         hz = (unsigned long long)HZ;    /* <asm/param.h> */
106 #else
107         /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */
108         hz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;
109 #endif
110
111         error("apps.plugin: ERROR: unknown HZ value. Assuming %llu.", hz);
112         return hz;
113 }
114
115
116 // ----------------------------------------------------------------------------
117 // target
118 // target is the point to aggregate a process tree values
119
120 struct target {
121         char compare[MAX_COMPARE_NAME + 1];
122         char id[MAX_NAME + 1];
123         char name[MAX_NAME + 1];
124
125         unsigned long long minflt;
126         unsigned long long cminflt;
127         unsigned long long majflt;
128         unsigned long long cmajflt;
129         unsigned long long utime;
130         unsigned long long stime;
131         unsigned long long cutime;
132         unsigned long long cstime;
133         unsigned long long num_threads;
134         unsigned long long rss;
135
136         unsigned long long fix_minflt;
137         unsigned long long fix_cminflt;
138         unsigned long long fix_majflt;
139         unsigned long long fix_cmajflt;
140         unsigned long long fix_utime;
141         unsigned long long fix_stime;
142         unsigned long long fix_cutime;
143         unsigned long long fix_cstime;
144
145         unsigned long long statm_size;
146         unsigned long long statm_resident;
147         unsigned long long statm_share;
148         unsigned long long statm_text;
149         unsigned long long statm_lib;
150         unsigned long long statm_data;
151         unsigned long long statm_dirty;
152
153         unsigned long long io_logical_bytes_read;
154         unsigned long long io_logical_bytes_written;
155         unsigned long long io_read_calls;
156         unsigned long long io_write_calls;
157         unsigned long long io_storage_bytes_read;
158         unsigned long long io_storage_bytes_written;
159         unsigned long long io_cancelled_write_bytes;
160
161         unsigned long long fix_io_logical_bytes_read;
162         unsigned long long fix_io_logical_bytes_written;
163         unsigned long long fix_io_read_calls;
164         unsigned long long fix_io_write_calls;
165         unsigned long long fix_io_storage_bytes_read;
166         unsigned long long fix_io_storage_bytes_written;
167         unsigned long long fix_io_cancelled_write_bytes;
168
169         int *fds;
170         unsigned long long openfiles;
171         unsigned long long openpipes;
172         unsigned long long opensockets;
173         unsigned long long openinotifies;
174         unsigned long long openeventfds;
175         unsigned long long opentimerfds;
176         unsigned long long opensignalfds;
177         unsigned long long openeventpolls;
178         unsigned long long openother;
179
180         unsigned long processes;        // how many processes have been merged to this
181         int exposed;                            // if set, we have sent this to netdata
182         int hidden;                                     // if set, we set the hidden flag on the dimension
183         int debug;
184
185         struct target *target;  // the one that will be reported to netdata
186         struct target *next;
187 } *target_root = NULL, *default_target = NULL;
188
189 long targets = 0;
190
191 // find or create a target
192 // there are targets that are just agregated to other target (the second argument)
193 struct target *get_target(const char *id, struct target *target)
194 {
195         const char *nid = id;
196         if(nid[0] == '-') nid++;
197
198         struct target *w;
199         for(w = target_root ; w ; w = w->next)
200                 if(strncmp(nid, w->id, MAX_NAME) == 0) return w;
201         
202         w = calloc(sizeof(struct target), 1);
203         if(!w) {
204                 error("apps.plugin: cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct target));
205                 return NULL;
206         }
207
208         strncpy(w->id, nid, MAX_NAME);
209         strncpy(w->name, nid, MAX_NAME);
210         strncpy(w->compare, nid, MAX_COMPARE_NAME);
211         if(id[0] == '-') w->hidden = 1;
212
213         w->target = target;
214
215         w->next = target_root;
216         target_root = w;
217
218         if(debug) fprintf(stderr, "apps.plugin: adding hook for process '%s', compare '%s' on target '%s'\n", w->id, w->compare, w->target?w->target->id:"");
219
220         return w;
221 }
222
223 // read the process groups file
224 int read_process_groups(const char *name)
225 {
226         char buffer[4096+1];
227         char filename[FILENAME_MAX + 1];
228
229         snprintf(filename, FILENAME_MAX, "%s/apps_%s.conf", CONFIG_DIR, name);
230
231         if(debug) fprintf(stderr, "apps.plugin: process groups file: '%s'\n", filename);
232         FILE *fp = fopen(filename, "r");
233         if(!fp) {
234                 error("apps.plugin: ERROR: cannot open file '%s' (%s)", filename, strerror(errno));
235                 return 1;
236         }
237
238         long line = 0;
239         while(fgets(buffer, 4096, fp) != NULL) {
240                 int whidden = 0, wdebug = 0;
241                 line++;
242
243                 // if(debug) fprintf(stderr, "apps.plugin: \tread %s\n", buffer);
244
245                 char *s = buffer, *t, *p;
246                 s = trim(s);
247                 if(!s || !*s || *s == '#') continue;
248
249                 if(debug) fprintf(stderr, "apps.plugin: \tread %s\n", s);
250
251                 // the target name
252                 t = strsep(&s, ":");
253                 if(t) t = trim(t);
254                 if(!t || !*t) continue;
255
256                 while(t[0]) {
257                         int stop = 1;
258
259                         switch(t[0]) {
260                                 case '-':
261                                         stop = 0;
262                                         whidden = 1;
263                                         t++;
264                                         break;
265
266                                 case '+':
267                                         stop = 0;
268                                         wdebug = 1;
269                                         t++;
270                                         break;
271                         }
272
273                         if(stop) break;
274                 }
275
276                 if(debug) fprintf(stderr, "apps.plugin: \t\ttarget %s\n", t);
277
278                 struct target *w = NULL;
279                 long count = 0;
280
281                 // the process names
282                 while((p = strsep(&s, " "))) {
283                         p = trim(p);
284                         if(!p || !*p) continue;
285
286                         struct target *n = get_target(p, w);
287                         n->hidden = whidden;
288                         n->debug = wdebug;
289                         if(!w) w = n;
290
291                         count++;
292                 }
293
294                 if(w) strncpy(w->name, t, MAX_NAME);
295                 if(!count) error("apps.plugin: ERROR: the line %ld on file '%s', for group '%s' does not state any process names.", line, filename, t);
296         }
297         fclose(fp);
298
299         default_target = get_target("+p!o@w#e$i^r&7*5(-i)l-o_", NULL); // match nothing
300         strncpy(default_target->name, "other", MAX_NAME);
301
302         return 0;
303 }
304
305
306 // ----------------------------------------------------------------------------
307 // data to store for each pid
308 // see: man proc
309
310 struct pid_stat {
311         int32_t pid;
312         char comm[MAX_COMPARE_NAME + 1];
313         char state;
314         int32_t ppid;
315         int32_t pgrp;
316         int32_t session;
317         int32_t tty_nr;
318         int32_t tpgid;
319         uint64_t flags;
320         unsigned long long minflt;
321         unsigned long long cminflt;
322         unsigned long long majflt;
323         unsigned long long cmajflt;
324         unsigned long long utime;
325         unsigned long long stime;
326         unsigned long long cutime;
327         unsigned long long cstime;
328         int64_t priority;
329         int64_t nice;
330         int32_t num_threads;
331         int64_t itrealvalue;
332         unsigned long long starttime;
333         unsigned long long vsize;
334         unsigned long long rss;
335         unsigned long long rsslim;
336         unsigned long long starcode;
337         unsigned long long endcode;
338         unsigned long long startstack;
339         unsigned long long kstkesp;
340         unsigned long long kstkeip;
341         uint64_t signal;
342         uint64_t blocked;
343         uint64_t sigignore;
344         uint64_t sigcatch;
345         uint64_t wchan;
346         uint64_t nswap;
347         uint64_t cnswap;
348         int32_t exit_signal;
349         int32_t processor;
350         uint32_t rt_priority;
351         uint32_t policy;
352         unsigned long long delayacct_blkio_ticks;
353         uint64_t guest_time;
354         int64_t cguest_time;
355
356         unsigned long long statm_size;
357         unsigned long long statm_resident;
358         unsigned long long statm_share;
359         unsigned long long statm_text;
360         unsigned long long statm_lib;
361         unsigned long long statm_data;
362         unsigned long long statm_dirty;
363
364         unsigned long long io_logical_bytes_read;
365         unsigned long long io_logical_bytes_written;
366         unsigned long long io_read_calls;
367         unsigned long long io_write_calls;
368         unsigned long long io_storage_bytes_read;
369         unsigned long long io_storage_bytes_written;
370         unsigned long long io_cancelled_write_bytes;
371
372 #ifdef INCLUDE_CHILDS
373         unsigned long long old_utime;
374         unsigned long long old_stime;
375         unsigned long long old_minflt;
376         unsigned long long old_majflt;
377
378         unsigned long long old_cutime;
379         unsigned long long old_cstime;
380         unsigned long long old_cminflt;
381         unsigned long long old_cmajflt;
382
383         unsigned long long fix_cutime;
384         unsigned long long fix_cstime;
385         unsigned long long fix_cminflt;
386         unsigned long long fix_cmajflt;
387
388         unsigned long long diff_cutime;
389         unsigned long long diff_cstime;
390         unsigned long long diff_cminflt;
391         unsigned long long diff_cmajflt;
392 #endif
393
394         int *fds;                                       // array of fds it uses
395         int fds_size;                           // the size of the fds array
396
397         int childs;                                     // number of processes directly referencing this
398         int updated;                            // 1 when update
399         int merged;                                     // 1 when it has been merged to its parent
400         int new_entry;
401         struct target *target;
402         struct pid_stat *parent;
403         struct pid_stat *prev;
404         struct pid_stat *next;
405 } *root = NULL, **all_pids;
406
407 long pids = 0;
408
409 struct pid_stat *get_entry(pid_t pid)
410 {
411         if(all_pids[pid]) {
412                 all_pids[pid]->new_entry = 0;
413                 return all_pids[pid];
414         }
415
416         all_pids[pid] = calloc(sizeof(struct pid_stat), 1);
417         if(!all_pids[pid]) {
418                 error("apps.plugin: ERROR: Cannot allocate %lu bytes of memory", (unsigned long)sizeof(struct pid_stat));
419                 return NULL;
420         }
421
422         all_pids[pid]->fds = calloc(sizeof(int), 100);
423         if(!all_pids[pid]->fds)
424                 error("apps.plugin: ERROR: Cannot allocate %ld bytes of memory", (unsigned long)(sizeof(int) * 100));
425         else all_pids[pid]->fds_size = 100;
426
427         if(root) root->prev = all_pids[pid];
428         all_pids[pid]->next = root;
429         root = all_pids[pid];
430
431         all_pids[pid]->new_entry = 1;
432
433         return all_pids[pid];
434 }
435
436 void del_entry(pid_t pid)
437 {
438         if(!all_pids[pid]) return;
439
440         if(debug) fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, all_pids[pid]->comm);
441
442         if(root == all_pids[pid]) root = all_pids[pid]->next;
443         if(all_pids[pid]->next) all_pids[pid]->next->prev = all_pids[pid]->prev;
444         if(all_pids[pid]->prev) all_pids[pid]->prev->next = all_pids[pid]->next;
445
446         if(all_pids[pid]->fds) free(all_pids[pid]->fds);
447         free(all_pids[pid]);
448         all_pids[pid] = NULL;
449 }
450
451 #ifdef INCLUDE_CHILDS
452 // print a tree view of all processes
453 int walk_down(pid_t pid, int level) {
454         struct pid_stat *p = NULL;
455         char b[level+3];
456         int i, ret = 0;
457
458         for(i = 0; i < level; i++) b[i] = '\t';
459         b[level] = '|';
460         b[level+1] = '-';
461         b[level+2] = '\0';
462
463         for(p = root; p ; p = p->next) {
464                 if(p->ppid == pid) {
465                         ret += walk_down(p->pid, level+1);
466                 }
467         }
468
469         p = all_pids[pid];
470         if(p) {
471                 if(!p->updated) ret += 1;
472                 if(ret) fprintf(stderr, "%s %s %d [%s, %s] c=%d u=%llu+%llu, s=%llu+%llu, cu=%llu+%llu, cs=%llu+%llu, n=%llu+%llu, j=%llu+%llu, cn=%llu+%llu, cj=%llu+%llu\n"
473                         , b, p->comm, p->pid, p->updated?"OK":"KILLED", p->target->name, p->childs
474                         , p->utime, p->utime - p->old_utime
475                         , p->stime, p->stime - p->old_stime
476                         , p->cutime, p->cutime - p->old_cutime
477                         , p->cstime, p->cstime - p->old_cstime
478                         , p->minflt, p->minflt - p->old_minflt
479                         , p->majflt, p->majflt - p->old_majflt
480                         , p->cminflt, p->cminflt - p->old_cminflt
481                         , p->cmajflt, p->cmajflt - p->old_cmajflt
482                         );
483         }
484
485         return ret;
486 }
487 #endif
488
489
490 // ----------------------------------------------------------------------------
491 // file descriptor
492 // this is used to keep a global list of all open files of the system
493 // it is needed in order to figure out the unique files a process tree has open
494
495 #define FILE_DESCRIPTORS_INCREASE_STEP 100
496
497 struct file_descriptor {
498         avl avl;
499         unsigned long magic;
500         unsigned long hash;
501         const char *name;
502         int type;
503         long count;
504         long pos;
505 } *all_files = NULL;
506
507 int all_files_len = 0;
508 int all_files_size = 0;
509
510 int file_descriptor_compare(void* a, void* b) {
511         if(((struct file_descriptor *)a)->magic != 0x0BADCAFE || ((struct file_descriptor *)b)->magic != 0x0BADCAFE)
512                 error("Corrupted index data detected. Please report this.");
513
514         if(((struct file_descriptor *)a)->hash < ((struct file_descriptor *)b)->hash)
515                 return -1;
516         else if(((struct file_descriptor *)a)->hash > ((struct file_descriptor *)b)->hash)
517                 return 1;
518         else return strcmp(((struct file_descriptor *)a)->name, ((struct file_descriptor *)b)->name);
519 }
520 int file_descriptor_iterator(avl *a) { if(a) {}; return 0; }
521
522 avl_tree all_files_index = {
523                 NULL,
524                 file_descriptor_compare
525 };
526
527 static struct file_descriptor *file_descriptor_find(const char *name, unsigned long hash) {
528         struct file_descriptor *result = NULL, tmp;
529         tmp.hash = (hash)?hash:simple_hash(name);
530         tmp.name = name;
531         tmp.count = 0;
532         tmp.pos = 0;
533         tmp.magic = 0x0BADCAFE;
534
535         avl_search(&all_files_index, (avl *)&tmp, file_descriptor_iterator, (avl **)&result);
536         return result;
537 }
538
539 #define file_descriptor_add(fd) avl_insert(&all_files_index, (avl *)(fd))
540 #define file_descriptor_remove(fd) avl_remove(&all_files_index, (avl *)(fd))
541
542 #define FILETYPE_OTHER 0
543 #define FILETYPE_FILE 1
544 #define FILETYPE_PIPE 2
545 #define FILETYPE_SOCKET 3
546 #define FILETYPE_INOTIFY 4
547 #define FILETYPE_EVENTFD 5
548 #define FILETYPE_EVENTPOLL 6
549 #define FILETYPE_TIMERFD 7
550 #define FILETYPE_SIGNALFD 8
551
552 void file_descriptor_not_used(int id)
553 {
554         if(id > 0 && id < all_files_size) {
555                 if(all_files[id].magic != 0x0BADCAFE) {
556                         error("Ignoring request to remove empty file id %d.", id);
557                         return;
558                 }
559
560                 if(debug) fprintf(stderr, "apps.plugin: decreasing slot %d (count = %ld).\n", id, all_files[id].count);
561
562                 if(all_files[id].count > 0) {
563                         all_files[id].count--;
564
565                         if(!all_files[id].count) {
566                                 if(debug) fprintf(stderr, "apps.plugin:   >> slot %d is empty.\n", id);
567                                 file_descriptor_remove(&all_files[id]);
568                                 all_files[id].magic = 0x00000000;
569                                 all_files_len--;
570                         }
571                 }
572                 else
573                         error("apps.plugin: ERROR: request to decrease counter of fd %d (%s), while the use counter is 0", id, all_files[id].name);
574         }
575         else    error("apps.plugin: ERROR: request to decrease counter of fd %d, which is outside the array size (1 to %d)", id, all_files_size);
576 }
577
578 unsigned long file_descriptor_find_or_add(const char *name)
579 {
580         static int last_pos = 0;
581         unsigned long hash = simple_hash(name);
582
583         if(debug) fprintf(stderr, "apps.plugin: adding or finding name '%s' with hash %lu\n", name, hash);
584
585         struct file_descriptor *fd = file_descriptor_find(name, hash);
586         if(fd) {
587                 // found
588                 if(debug) fprintf(stderr, "apps.plugin:   >> found on slot %ld\n", fd->pos);
589                 fd->count++;
590                 return fd->pos;
591         }
592         // not found
593
594         // check we have enough memory to add it
595         if(!all_files || all_files_len == all_files_size) {
596                 void *old = all_files;
597
598                 // there is no empty slot
599                 if(debug) fprintf(stderr, "apps.plugin: extending fd array to %d entries\n", all_files_size + FILE_DESCRIPTORS_INCREASE_STEP);
600                 all_files = realloc(all_files, (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP) * sizeof(struct file_descriptor));
601
602                 // if the address changed, we have to rebuild the index
603                 // since all pointers are now invalid
604                 if(old && old != (void *)all_files) {
605                         if(debug) fprintf(stderr, "apps.plugin:   >> re-indexing.\n");
606                         all_files_index.root = NULL;
607                         int i;
608                         for(i = 0; i < all_files_size; i++) {
609                                 if(!all_files[i].count) continue;
610                                 file_descriptor_add(&all_files[i]);
611                         }
612                         for(i = all_files_size; i < (all_files_size + FILE_DESCRIPTORS_INCREASE_STEP); i++) {
613                                 all_files[i].count = 0;
614                                 all_files[i].name = NULL;
615                                 all_files[i].magic = 0x00000000;
616                                 all_files[i].pos = i;
617                         }
618                         if(debug) fprintf(stderr, "apps.plugin:   >> re-indexing done.\n");
619                 }
620
621                 if(!all_files_size) all_files_len = 1;
622                 all_files_size += FILE_DESCRIPTORS_INCREASE_STEP;
623         }
624
625         if(debug) fprintf(stderr, "apps.plugin:   >> searching for empty slot.\n");
626
627         // search for an empty slot
628         int i, c;
629         for(i = 0, c = last_pos ; i < all_files_size ; i++, c++) {
630                 if(c >= all_files_size) c = 0;
631                 if(c == 0) continue;
632
633                 if(!all_files[c].count) {
634                         if(debug) fprintf(stderr, "apps.plugin:   >> Examining slot %d.\n", c);
635
636                         if(all_files[c].magic == 0x0BADCAFE && all_files[c].name && file_descriptor_find(all_files[c].name, all_files[c].hash))
637                                 error("apps.plugin: fd on position %d is not cleared properly. It still has %s in it.\n", c, all_files[c].name);
638
639                         if(debug) fprintf(stderr, "apps.plugin:   >> %s fd position %d for %s (last name: %s)\n", all_files[c].name?"re-using":"using", c, name, all_files[c].name);
640                         if(all_files[c].name) free((void *)all_files[c].name);
641                         all_files[c].name = NULL;
642                         last_pos = c;
643                         break;
644                 }
645         }
646         if(i == all_files_size) {
647                 fatal("We should find an empty slot, but there isn't any");
648                 exit(1);
649         }
650         if(debug) fprintf(stderr, "apps.plugin:   >> updating slot %d.\n", c);
651
652         all_files_len++;
653
654         // else we have an empty slot in 'c'
655
656         int type = FILETYPE_OTHER;
657         if(name[0] == '/') type = FILETYPE_FILE;
658         else if(strncmp(name, "pipe:", 5) == 0) type = FILETYPE_PIPE;
659         else if(strncmp(name, "socket:", 7) == 0) type = FILETYPE_SOCKET;
660         else if(strcmp(name, "anon_inode:inotify") == 0 || strcmp(name, "inotify") == 0) type = FILETYPE_INOTIFY;
661         else if(strcmp(name, "anon_inode:[eventfd]") == 0) type = FILETYPE_EVENTFD;
662         else if(strcmp(name, "anon_inode:[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
663         else if(strcmp(name, "anon_inode:[timerfd]") == 0) type = FILETYPE_TIMERFD;
664         else if(strcmp(name, "anon_inode:[signalfd]") == 0) type = FILETYPE_SIGNALFD;
665         else if(strncmp(name, "anon_inode:", 11) == 0) {
666                 if(debug) fprintf(stderr, "apps.plugin: FIXME: unknown anonymous inode: %s\n", name);
667                 type = FILETYPE_OTHER;
668         }
669         else {
670                 if(debug) fprintf(stderr, "apps.plugin: FIXME: cannot understand linkname: %s\n", name);
671                 type = FILETYPE_OTHER;
672         }
673
674         all_files[c].name = strdup(name);
675         all_files[c].hash = hash;
676         all_files[c].type = type;
677         all_files[c].pos  = c;
678         all_files[c].count = 1;
679         all_files[c].magic = 0x0BADCAFE;
680
681         file_descriptor_add(&all_files[c]);
682
683         if(debug) fprintf(stderr, "apps.plugin: using fd position %d (name: %s)\n", c, all_files[c].name);
684
685         return c;
686 }
687
688
689 // ----------------------------------------------------------------------------
690 // update pids from proc
691
692 // 1. read all files in /proc
693 // 2. for each numeric directory:
694 //    i.   read /proc/pid/stat
695 //    ii.  read /proc/pid/statm
696 //    iii. read /proc/pid/io (requires root access)
697 //    iii. read the entries in directory /proc/pid/fd (requires root access)
698 //         for each entry:
699 //         a. find or create a struct file_descriptor
700 //         b. cleanup any old/unused file_descriptors
701
702 // after all these, some pids may be linked to targets, while others may not
703
704 // in case of errors, only 1 every 1000 errors is printed
705 // to avoid filling up all disk space
706 // if debug is enabled, all errors are printed
707
708 int update_from_proc(void)
709 {
710         static long count_errors = 0;
711
712         char buffer[PROC_BUFFER + 1];
713         char name[PROC_BUFFER + 1];
714         char filename[FILENAME_MAX+1];
715         DIR *dir = opendir("/proc");
716         if(!dir) return 0;
717
718         struct dirent *file = NULL;
719         struct pid_stat *p = NULL;
720
721         // mark them all as un-updated
722         pids = 0;
723         for(p = root; p ; p = p->next) {
724                 pids++;
725                 p->parent = NULL;
726                 p->updated = 0;
727                 p->childs = 0;
728                 p->merged = 0;
729                 p->new_entry = 0;
730         }
731
732         while((file = readdir(dir))) {
733                 char *endptr = file->d_name;
734                 pid_t pid = strtoul(file->d_name, &endptr, 10);
735                 if(pid <= 0 || pid > pid_max || endptr == file->d_name || *endptr != '\0') continue;
736
737
738                 // --------------------------------------------------------------------
739                 // /proc/<pid>/stat
740
741                 snprintf(filename, FILENAME_MAX, "/proc/%s/stat", file->d_name);
742                 int fd = open(filename, O_RDONLY);
743                 if(fd == -1) {
744                         if(errno != ENOENT && errno != ESRCH) {
745                                 if(!count_errors++ || debug)
746                                         error("apps.plugin: ERROR: cannot open file '%s' for reading (%d, %s).", filename, errno, strerror(errno));
747                         }
748                         continue;
749                 }
750                 file_counter++;
751
752                 int bytes = read(fd, buffer, PROC_BUFFER);
753                 close(fd);
754
755                 if(bytes == -1) {
756                         if(!count_errors++ || debug)
757                                 error("apps.plugin: ERROR: cannot read from file '%s' (%s).", filename, strerror(errno));
758                         continue;
759                 }
760
761                 if(bytes < 10) continue;
762                 buffer[bytes] = '\0';
763                 if(debug) fprintf(stderr, "apps.plugin: READ stat: %s", buffer);
764
765                 p = get_entry(pid);
766                 if(!p) continue;
767
768                 int parsed = sscanf(buffer,
769                         "%d (%[^)]) %c"                                         // pid, comm, state
770                         " %d %d %d %d %d"                                       // ppid, pgrp, session, tty_nr, tpgid
771                         " %" PRIu64 " %llu %llu %llu %llu"      // flags, minflt, cminflt, majflt, cmajflt
772                         " %llu %llu %llu %llu"                          // utime, stime, cutime, cstime
773                         " %" PRId64 " %" PRId64                         // priority, nice
774                         " %d"                                                           // num_threads
775                         " %" PRId64                                                     // itrealvalue
776                         " %llu"                                                         // starttime
777                         " %llu"                                                         // vsize
778                         " %llu"                                                         // rss
779                         " %llu %llu %llu %llu %llu %llu"        // rsslim, starcode, endcode, startstack, kstkesp, kstkeip
780                         " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 // signal, blocked, sigignore, sigcatch
781                         " %" PRIu64 " %" PRIu64 " %" PRIu64     // wchan, nswap, cnswap
782                         " %d %d"                                                        // exit_signal, processor
783                         " %u %u"                                                        // rt_priority, policy
784                         " %llu %" PRIu64 " %" PRId64            // delayacct_blkio_ticks, guest_time, cguest_time
785                         , &p->pid, name, &p->state
786                         , &p->ppid, &p->pgrp, &p->session, &p->tty_nr, &p->tpgid
787                         , &p->flags, &p->minflt, &p->cminflt, &p->majflt, &p->cmajflt
788                         , &p->utime, &p->stime, &p->cutime, &p->cstime
789                         , &p->priority, &p->nice
790                         , &p->num_threads
791                         , &p->itrealvalue
792                         , &p->starttime
793                         , &p->vsize
794                         , &p->rss
795                         , &p->rsslim, &p->starcode, &p->endcode, &p->startstack, &p->kstkesp, &p->kstkeip
796                         , &p->signal, &p->blocked, &p->sigignore, &p->sigcatch
797                         , &p->wchan, &p->nswap, &p->cnswap
798                         , &p->exit_signal, &p->processor
799                         , &p->rt_priority, &p->policy
800                         , &p->delayacct_blkio_ticks, &p->guest_time, &p->cguest_time
801                         );
802                 strncpy(p->comm, name, MAX_COMPARE_NAME);
803                 p->comm[MAX_COMPARE_NAME] = '\0';
804
805                 if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: VALUES: %s utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->comm, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
806
807                 if(parsed < 39) {
808                         if(!count_errors++ || debug || (p->target && p->target->debug))
809                                 error("apps.plugin: ERROR: file %s gave %d results (expected 44)\n", filename, parsed);
810                 }
811
812                 // check if it is target
813                 // we do this only once, the first time this pid is loaded
814                 if(p->new_entry) {
815                         if(debug) fprintf(stderr, "apps.plugin: \tJust added %s\n", p->comm);
816
817                         struct target *w;
818                         for(w = target_root; w ; w = w->next) {
819                                 // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm);
820
821                                 if(strcmp(w->compare, p->comm) == 0) {
822                                         if(w->target) p->target = w->target;
823                                         else p->target = w;
824
825                                         if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
826                                 }
827                         }
828                 }
829
830                 // just a few checks
831                 if(p->ppid < 0 || p->ppid > pid_max) p->ppid = 0;
832
833
834                 // --------------------------------------------------------------------
835                 // /proc/<pid>/statm
836
837                 snprintf(filename, FILENAME_MAX, "/proc/%s/statm", file->d_name);
838                 fd = open(filename, O_RDONLY);
839                 if(fd == -1) {
840                         if(errno != ENOENT && errno != ESRCH) {
841                                 if(!count_errors++ || debug || (p->target && p->target->debug))
842                                         error("apps.plugin: ERROR: cannot open file '%s' for reading (%d, %s).", filename, errno, strerror(errno));
843                         }
844                 }
845                 else {
846                         file_counter++;
847                         bytes = read(fd, buffer, PROC_BUFFER);
848                         close(fd);
849
850                         if(bytes == -1) {
851                                 if(!count_errors++ || debug || (p->target && p->target->debug))
852                                         error("apps.plugin: ERROR: cannot read from file '%s' (%s).", filename, strerror(errno));
853                         }
854                         else if(bytes > 10) {
855                                 buffer[bytes] = '\0';
856                                 if(debug || (p->target && p->target->debug))
857                                         fprintf(stderr, "apps.plugin: READ statm: %s", buffer);
858
859                                 parsed = sscanf(buffer,
860                                         "%llu %llu %llu %llu %llu %llu %llu"
861                                         , &p->statm_size
862                                         , &p->statm_resident
863                                         , &p->statm_share
864                                         , &p->statm_text
865                                         , &p->statm_lib
866                                         , &p->statm_data
867                                         , &p->statm_dirty
868                                         );
869
870                                 if(parsed < 7) {
871                                         if(!count_errors++ || debug || (p->target && p->target->debug))
872                                                 error("apps.plugin: ERROR: file %s gave %d results (expected 7)", filename, parsed);
873                                 }
874                         }
875                 }
876
877                 // --------------------------------------------------------------------
878                 // /proc/<pid>/io
879
880                 snprintf(filename, FILENAME_MAX, "/proc/%s/io", file->d_name);
881                 fd = open(filename, O_RDONLY);
882                 if(fd == -1) {
883                         if(errno != ENOENT && errno != ESRCH) {
884                                 if(!count_errors++ || debug || (p->target && p->target->debug))
885                                         error("apps.plugin: ERROR: cannot open file '%s' for reading (%d, %s).", filename, errno, strerror(errno));
886                         }
887                 }
888                 else {
889                         file_counter++;
890                         bytes = read(fd, buffer, PROC_BUFFER);
891                         close(fd);
892
893                         if(bytes == -1) {
894                                 if(!count_errors++ || debug || (p->target && p->target->debug))
895                                         error("apps.plugin: ERROR: cannot read from file '%s' (%s).", filename, strerror(errno));
896                         }
897                         else if(bytes > 10) {
898                                 buffer[bytes] = '\0';
899                                 if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: READ io: %s", buffer);
900
901                                 parsed = sscanf(buffer,
902                                         "rchar: %llu\nwchar: %llu\nsyscr: %llu\nsyscw: %llu\nread_bytes: %llu\nwrite_bytes: %llu\ncancelled_write_bytes: %llu"
903                                         , &p->io_logical_bytes_read
904                                         , &p->io_logical_bytes_written
905                                         , &p->io_read_calls
906                                         , &p->io_write_calls
907                                         , &p->io_storage_bytes_read
908                                         , &p->io_storage_bytes_written
909                                         , &p->io_cancelled_write_bytes
910                                         );
911
912                                 if(parsed < 7) {
913                                         if(!count_errors++ || debug || (p->target && p->target->debug))
914                                                 error("apps.plugin: ERROR: file %s gave %d results (expected 7)", filename, parsed);
915                                 }
916                         }
917                 }
918
919                 // --------------------------------------------------------------------
920                 // /proc/<pid>/fd
921
922                 snprintf(filename, FILENAME_MAX, "/proc/%s/fd", file->d_name);
923                 DIR *fds = opendir(filename);
924                 if(fds) {
925                         int c;
926                         struct dirent *de;
927                         char fdname[FILENAME_MAX + 1];
928                         char linkname[FILENAME_MAX + 1];
929
930                         // make the array negative
931                         for(c = 0 ; c < p->fds_size ; c++) p->fds[c] = -p->fds[c];
932
933                         while((de = readdir(fds))) {
934                                 if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue;
935
936                                 // check if the fds array is small
937                                 int fdid = atol(de->d_name);
938                                 if(fdid < 0) continue;
939                                 if(fdid >= p->fds_size) {
940                                         // it is small, extend it
941                                         if(debug) fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + 100);
942                                         p->fds = realloc(p->fds, (fdid + 100) * sizeof(int));
943                                         if(!p->fds) {
944                                                 error("apps.plugin: ERROR: cannot re-allocate fds for %s", p->comm);
945                                                 break;
946                                         }
947
948                                         // and initialize it
949                                         for(c = p->fds_size ; c < (fdid + 100) ; c++) p->fds[c] = 0;
950                                         p->fds_size = fdid + 100;
951                                 }
952
953                                 if(p->fds[fdid] == 0) {
954                                         // we don't know this fd, get it
955
956                                         sprintf(fdname, "/proc/%s/fd/%s", file->d_name, de->d_name);
957                                         int l = readlink(fdname, linkname, FILENAME_MAX);
958                                         if(l == -1) {
959                                                 if(debug || (p->target && p->target->debug)) {
960                                                         if(!count_errors++ || debug || (p->target && p->target->debug))
961                                                                 error("apps.plugin: ERROR: cannot read link %s", fdname);
962                                                 }
963                                                 continue;
964                                         }
965                                         linkname[l] = '\0';
966                                         file_counter++;
967
968                                         // if another process already has this, we will get
969                                         // the same id
970                                         p->fds[fdid] = file_descriptor_find_or_add(linkname);
971                                 }
972
973                                 // else make it positive again, we need it
974                                 // of course, the actual file may have changed, but we don't care so much
975                                 // FIXME: we could compare the inode as returned by readdir direct structure
976                                 else p->fds[fdid] = -p->fds[fdid];
977                         }
978                         closedir(fds);
979
980                         // remove all the negative file descriptors
981                         for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] < 0) {
982                                 file_descriptor_not_used(-p->fds[c]);
983                                 p->fds[c] = 0;
984                         }
985                 }
986
987                 // --------------------------------------------------------------------
988                 // done!
989
990                 // mark it as updated
991                 p->updated = 1;
992         }
993         if(count_errors > 1000) {
994                 error("apps.plugin: ERROR: %ld more errors encountered\n", count_errors - 1);
995                 count_errors = 0;
996         }
997
998         closedir(dir);
999
1000         return 1;
1001 }
1002
1003
1004 // ----------------------------------------------------------------------------
1005 // update statistics on the targets
1006
1007 // 1. link all childs to their parents
1008 // 2. go from bottom to top, marking as merged all childs to their parents
1009 //    this step links all parents without a target to the child target, if any
1010 // 3. link all top level processes (the ones not merged) to the default target
1011 // 4. go from top to bottom, linking all childs without a target, to their parent target
1012 //    after this step, all processes have a target
1013 // [5. for each killed pid (updated = 0), remove its usage from its target]
1014 // 6. zero all targets
1015 // 7. concentrate all values on the targets
1016 // 8. remove all killed processes
1017 // 9. find the unique file count for each target
1018
1019 void update_statistics(void)
1020 {
1021         int c;
1022         struct pid_stat *p = NULL;
1023
1024         // link all parents and update childs count
1025         for(p = root; p ; p = p->next) {
1026                 if(p->ppid > 0 && p->ppid <= pid_max && all_pids[p->ppid]) {
1027                         if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \tparent of %d %s is %d %s\n", p->pid, p->comm, p->ppid, all_pids[p->ppid]->comm);
1028                         
1029                         p->parent = all_pids[p->ppid];
1030                         p->parent->childs++;
1031                 }
1032                 else if(p->ppid != 0) error("apps.plugin: \t\tWRONG! pid %d %s states parent %d, but the later does not exist.", p->pid, p->comm, p->ppid);
1033         }
1034
1035         // find all the procs with 0 childs and merge them to their parents
1036         // repeat, until nothing more can be done.
1037         int found = 1;
1038         while(found) {
1039                 found = 0;
1040                 for(p = root; p ; p = p->next) {
1041                         // if this process does not have any childs, and
1042                         // is not already merged, and
1043                         // its parent has childs waiting to be merged, and
1044                         // the target of this process and its parent is the same, or the parent does not have a target, or this process does not have a parent
1045                         // and its parent is not init
1046                         // then... merge them!
1047                         if(!p->childs && !p->merged && p->parent && p->parent->childs && (p->target == p->parent->target || !p->parent->target || !p->target) && p->ppid != 1) {
1048                                 p->parent->childs--;
1049                                 p->merged = 1;
1050
1051                                 // the parent inherits the child's target, if it does not have a target itself
1052                                 if(p->target && !p->parent->target) {
1053                                         p->parent->target = p->target;
1054                                         if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\ttarget %s is inherited by %d %s from its child %d %s.\n", p->target->name, p->parent->pid, p->parent->comm, p->pid, p->comm);
1055                                 }
1056
1057                                 found++;
1058                         }
1059                 }
1060                 if(debug) fprintf(stderr, "apps.plugin: merged %d processes\n", found);
1061         }
1062
1063         // give a default target on all top level processes
1064         // init goes always to default target
1065         if(all_pids[1]) all_pids[1]->target = default_target;
1066
1067         for(p = root; p ; p = p->next) {
1068                 // if the process is not merged itself
1069                 // then is is a top level process
1070                 if(!p->merged && !p->target) p->target = default_target;
1071
1072 #ifdef INCLUDE_CHILDS
1073                 // by the way, update the diffs
1074                 // will be used later for substracting killed process times
1075                 p->diff_cutime = p->utime - p->cutime;
1076                 p->diff_cstime = p->stime - p->cstime;
1077                 p->diff_cminflt = p->minflt - p->cminflt;
1078                 p->diff_cmajflt = p->majflt - p->cmajflt;
1079 #endif
1080         }
1081
1082         // give a target to all merged child processes
1083         found = 1;
1084         while(found) {
1085                 found = 0;
1086                 for(p = root; p ; p = p->next) {
1087                         if(!p->target && p->merged && p->parent && p->parent->target) {
1088                                 p->target = p->parent->target;
1089                                 found++;
1090                         }
1091                 }
1092         }
1093
1094 #ifdef INCLUDE_CHILDS
1095         // for each killed process, remove its values from the parents
1096         // sums (we had already added them in a previous loop)
1097         for(p = root; p ; p = p->next) {
1098                 if(p->updated) continue;
1099
1100                 if(debug) fprintf(stderr, "apps.plugin: UNMERGING %d %s\n", p->pid, p->comm);
1101
1102                 unsigned long long diff_utime = p->utime + p->cutime + p->fix_cutime;
1103                 unsigned long long diff_stime = p->stime + p->cstime + p->fix_cstime;
1104                 unsigned long long diff_minflt = p->minflt + p->cminflt + p->fix_cminflt;
1105                 unsigned long long diff_majflt = p->majflt + p->cmajflt + p->fix_cmajflt;
1106
1107                 struct pid_stat *t = p;
1108                 while((t = t->parent)) {
1109                         if(!t->updated) continue;
1110
1111                         unsigned long long x;
1112                         if(diff_utime && t->diff_cutime) {
1113                                 x = (t->diff_cutime < diff_utime)?t->diff_cutime:diff_utime;
1114                                 diff_utime -= x;
1115                                 t->diff_cutime -= x;
1116                                 t->fix_cutime += x;
1117                                 if(debug) fprintf(stderr, "apps.plugin: \t cutime %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
1118                         }
1119                         if(diff_stime && t->diff_cstime) {
1120                                 x = (t->diff_cstime < diff_stime)?t->diff_cstime:diff_stime;
1121                                 diff_stime -= x;
1122                                 t->diff_cstime -= x;
1123                                 t->fix_cstime += x;
1124                                 if(debug) fprintf(stderr, "apps.plugin: \t cstime %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
1125                         }
1126                         if(diff_minflt && t->diff_cminflt) {
1127                                 x = (t->diff_cminflt < diff_minflt)?t->diff_cminflt:diff_minflt;
1128                                 diff_minflt -= x;
1129                                 t->diff_cminflt -= x;
1130                                 t->fix_cminflt += x;
1131                                 if(debug) fprintf(stderr, "apps.plugin: \t cminflt %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
1132                         }
1133                         if(diff_majflt && t->diff_cmajflt) {
1134                                 x = (t->diff_cmajflt < diff_majflt)?t->diff_cmajflt:diff_majflt;
1135                                 diff_majflt -= x;
1136                                 t->diff_cmajflt -= x;
1137                                 t->fix_cmajflt += x;
1138                                 if(debug) fprintf(stderr, "apps.plugin: \t cmajflt %llu from %d %s %s\n", x, t->pid, t->comm, t->target->name);
1139                         }
1140                 }
1141
1142                 if(diff_utime) error("apps.plugin: \t cannot fix up utime %llu", diff_utime);
1143                 if(diff_stime) error("apps.plugin: \t cannot fix up stime %llu", diff_stime);
1144                 if(diff_minflt) error("apps.plugin: \t cannot fix up minflt %llu", diff_minflt);
1145                 if(diff_majflt) error("apps.plugin: \t cannot fix up majflt %llu", diff_majflt);
1146         }
1147 #endif
1148
1149         // zero all the targets
1150         targets = 0;
1151         struct target *w;
1152         for (w = target_root; w ; w = w->next) {
1153                 targets++;
1154
1155                 w->fds = calloc(sizeof(int), all_files_size);
1156                 if(!w->fds)
1157                         error("apps.plugin: ERROR: cannot allocate memory for fds in %s", w->name);
1158         
1159                 w->minflt = 0;
1160                 w->majflt = 0;
1161                 w->utime = 0;
1162                 w->stime = 0;
1163                 w->cminflt = 0;
1164                 w->cmajflt = 0;
1165                 w->cutime = 0;
1166                 w->cstime = 0;
1167                 w->num_threads = 0;
1168                 w->rss = 0;
1169                 w->processes = 0;
1170
1171                 w->statm_size = 0;
1172                 w->statm_resident = 0;
1173                 w->statm_share = 0;
1174                 w->statm_text = 0;
1175                 w->statm_lib = 0;
1176                 w->statm_data = 0;
1177                 w->statm_dirty = 0;
1178
1179                 w->io_logical_bytes_read = 0;
1180                 w->io_logical_bytes_written = 0;
1181                 w->io_read_calls = 0;
1182                 w->io_write_calls = 0;
1183                 w->io_storage_bytes_read = 0;
1184                 w->io_storage_bytes_written = 0;
1185                 w->io_cancelled_write_bytes = 0;
1186         }
1187
1188 #ifdef INCLUDE_CHILDS
1189         if(debug) walk_down(0, 1);
1190 #endif
1191
1192         // concentrate everything on the targets
1193         for(p = root; p ; p = p->next) {
1194                 if(!p->target) {
1195                         error("apps.plugin: ERROR: pid %d %s was left without a target!", p->pid, p->comm);
1196                         continue;
1197                 }
1198
1199                 if(p->updated) {
1200                         p->target->cutime += p->cutime; // - p->fix_cutime;
1201                         p->target->cstime += p->cstime; // - p->fix_cstime;
1202                         p->target->cminflt += p->cminflt; // - p->fix_cminflt;
1203                         p->target->cmajflt += p->cmajflt; // - p->fix_cmajflt;
1204
1205                         p->target->utime += p->utime; //+ (p->pid != 1)?(p->cutime - p->fix_cutime):0;
1206                         p->target->stime += p->stime; //+ (p->pid != 1)?(p->cstime - p->fix_cstime):0;
1207                         p->target->minflt += p->minflt; //+ (p->pid != 1)?(p->cminflt - p->fix_cminflt):0;
1208                         p->target->majflt += p->majflt; //+ (p->pid != 1)?(p->cmajflt - p->fix_cmajflt):0;
1209
1210                         p->target->num_threads += p->num_threads;
1211                         p->target->rss += p->rss;
1212
1213                         p->target->statm_size += p->statm_size;
1214                         p->target->statm_resident += p->statm_resident;
1215                         p->target->statm_share += p->statm_share;
1216                         p->target->statm_text += p->statm_text;
1217                         p->target->statm_lib += p->statm_lib;
1218                         p->target->statm_data += p->statm_data;
1219                         p->target->statm_dirty += p->statm_dirty;
1220
1221                         p->target->io_logical_bytes_read += p->io_logical_bytes_read;
1222                         p->target->io_logical_bytes_written += p->io_logical_bytes_written;
1223                         p->target->io_read_calls += p->io_read_calls;
1224                         p->target->io_write_calls += p->io_write_calls;
1225                         p->target->io_storage_bytes_read += p->io_storage_bytes_read;
1226                         p->target->io_storage_bytes_written += p->io_storage_bytes_written;
1227                         p->target->io_cancelled_write_bytes += p->io_cancelled_write_bytes;
1228
1229                         p->target->processes++;
1230
1231                         for(c = 0; c < p->fds_size ;c++) {
1232                                 if(p->fds[c] == 0) continue;
1233                                 if(p->fds[c] < all_files_size) {
1234                                         if(p->target->fds) p->target->fds[p->fds[c]]++;
1235                                 }
1236                                 else
1237                                         error("apps.plugin: ERROR: invalid fd number %d", p->fds[c]);
1238                         }
1239
1240                         if(debug || p->target->debug) fprintf(stderr, "apps.plugin: \tAgregating %s pid %d on %s utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->comm, p->pid, p->target->name, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
1241
1242 /*                      if(p->utime - p->old_utime > 100) fprintf(stderr, "BIG CHANGE: %d %s utime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->utime - p->old_utime, p->old_utime, p->utime);
1243                         if(p->cutime - p->old_cutime > 100) fprintf(stderr, "BIG CHANGE: %d %s cutime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cutime - p->old_cutime, p->old_cutime, p->cutime);
1244                         if(p->stime - p->old_stime > 100) fprintf(stderr, "BIG CHANGE: %d %s stime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->stime - p->old_stime, p->old_stime, p->stime);
1245                         if(p->cstime - p->old_cstime > 100) fprintf(stderr, "BIG CHANGE: %d %s cstime increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cstime - p->old_cstime, p->old_cstime, p->cstime);
1246                         if(p->minflt - p->old_minflt > 5000) fprintf(stderr, "BIG CHANGE: %d %s minflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->minflt - p->old_minflt, p->old_minflt, p->minflt);
1247                         if(p->majflt - p->old_majflt > 5000) fprintf(stderr, "BIG CHANGE: %d %s majflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->majflt - p->old_majflt, p->old_majflt, p->majflt);
1248                         if(p->cminflt - p->old_cminflt > 15000) fprintf(stderr, "BIG CHANGE: %d %s cminflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cminflt - p->old_cminflt, p->old_cminflt, p->cminflt);
1249                         if(p->cmajflt - p->old_cmajflt > 15000) fprintf(stderr, "BIG CHANGE: %d %s cmajflt increased by %llu from %llu to %llu\n", p->pid, p->comm, p->cmajflt - p->old_cmajflt, p->old_cmajflt, p->cmajflt);
1250 */
1251 #ifdef INCLUDE_CHILDS
1252                         p->old_utime = p->utime;
1253                         p->old_cutime = p->cutime;
1254                         p->old_stime = p->stime;
1255                         p->old_cstime = p->cstime;
1256                         p->old_minflt = p->minflt;
1257                         p->old_majflt = p->majflt;
1258                         p->old_cminflt = p->cminflt;
1259                         p->old_cmajflt = p->cmajflt;
1260 #endif
1261                 }
1262                 else {
1263                         // since the process has exited, the user
1264                         // will see a drop in our charts, because the incremental
1265                         // values of this process will not be there
1266
1267                         // add them to the fix_* values and they will be added to
1268                         // the reported values, so that the report goes steady
1269                         p->target->fix_minflt += p->minflt;
1270                         p->target->fix_majflt += p->majflt;
1271                         p->target->fix_utime += p->utime;
1272                         p->target->fix_stime += p->stime;
1273                         p->target->fix_cminflt += p->cminflt;
1274                         p->target->fix_cmajflt += p->cmajflt;
1275                         p->target->fix_cutime += p->cutime;
1276                         p->target->fix_cstime += p->cstime;
1277
1278                         p->target->fix_io_logical_bytes_read += p->io_logical_bytes_read;
1279                         p->target->fix_io_logical_bytes_written += p->io_logical_bytes_written;
1280                         p->target->fix_io_read_calls += p->io_read_calls;
1281                         p->target->fix_io_write_calls += p->io_write_calls;
1282                         p->target->fix_io_storage_bytes_read += p->io_storage_bytes_read;
1283                         p->target->fix_io_storage_bytes_written += p->io_storage_bytes_written;
1284                         p->target->fix_io_cancelled_write_bytes += p->io_cancelled_write_bytes;
1285                 }
1286         }
1287
1288 //      fprintf(stderr, "\n");
1289         // cleanup all un-updated processed (exited, killed, etc)
1290         for(p = root; p ;) {
1291                 if(!p->updated) {
1292 //                      fprintf(stderr, "\tEXITED %d %s [parent %d %s, target %s] utime=%llu, stime=%llu, cutime=%llu, cstime=%llu, minflt=%llu, majflt=%llu, cminflt=%llu, cmajflt=%llu\n", p->pid, p->comm, p->parent->pid, p->parent->comm, p->target->name,  p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt);
1293                         
1294                         for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] > 0) {
1295                                 file_descriptor_not_used(p->fds[c]);
1296                                 p->fds[c] = 0;
1297                         }
1298
1299                         pid_t r = p->pid;
1300                         p = p->next;
1301                         del_entry(r);
1302                 }
1303                 else p = p->next;
1304         }
1305
1306         for (w = target_root; w ; w = w->next) {
1307                 w->openfiles = 0;
1308                 w->openpipes = 0;
1309                 w->opensockets = 0;
1310                 w->openinotifies = 0;
1311                 w->openeventfds = 0;
1312                 w->opentimerfds = 0;
1313                 w->opensignalfds = 0;
1314                 w->openeventpolls = 0;
1315                 w->openother = 0;
1316
1317                 for(c = 1; c < all_files_size ;c++) {
1318                         if(w->fds && w->fds[c] > 0) switch(all_files[c].type) {
1319                                 case FILETYPE_FILE:
1320                                         w->openfiles++;
1321                                         break;
1322
1323                                 case FILETYPE_PIPE:
1324                                         w->openpipes++;
1325                                         break;
1326
1327                                 case FILETYPE_SOCKET:
1328                                         w->opensockets++;
1329                                         break;
1330
1331                                 case FILETYPE_INOTIFY:
1332                                         w->openinotifies++;
1333                                         break;
1334
1335                                 case FILETYPE_EVENTFD:
1336                                         w->openeventfds++;
1337                                         break;
1338
1339                                 case FILETYPE_TIMERFD:
1340                                         w->opentimerfds++;
1341                                         break;
1342
1343                                 case FILETYPE_SIGNALFD:
1344                                         w->opensignalfds++;
1345                                         break;
1346
1347                                 case FILETYPE_EVENTPOLL:
1348                                         w->openeventpolls++;
1349                                         break;
1350
1351                                 default:
1352                                         w->openother++;
1353                         }
1354                 }
1355
1356                 free(w->fds);
1357                 w->fds = NULL;
1358         }
1359 }
1360
1361 // ----------------------------------------------------------------------------
1362 // update chart dimensions
1363
1364 void show_dimensions(void)
1365 {
1366         static struct timeval last = { 0, 0 };
1367         static struct rusage me_last;
1368
1369         struct target *w;
1370         struct timeval now;
1371         struct rusage me;
1372
1373         unsigned long long usec;
1374         unsigned long long cpuuser;
1375         unsigned long long cpusyst;
1376
1377         if(!last.tv_sec) {
1378                 gettimeofday(&last, NULL);
1379                 getrusage(RUSAGE_SELF, &me_last);
1380
1381                 // the first time, give a zero to allow
1382                 // netdata calibrate to the current time
1383                 // usec = update_every * 1000000ULL;
1384                 usec = 0ULL;
1385                 cpuuser = 0;
1386                 cpusyst = 0;
1387         }
1388         else {
1389                 gettimeofday(&now, NULL);
1390                 getrusage(RUSAGE_SELF, &me);
1391
1392                 usec = usecdiff(&now, &last);
1393                 cpuuser = me.ru_utime.tv_sec * 1000000ULL + me.ru_utime.tv_usec;
1394                 cpusyst = me.ru_stime.tv_sec * 1000000ULL + me.ru_stime.tv_usec;
1395
1396                 bcopy(&now, &last, sizeof(struct timeval));
1397                 bcopy(&me, &me_last, sizeof(struct rusage));
1398         }
1399
1400         fprintf(stdout, "BEGIN apps.cpu %llu\n", usec);
1401         for (w = target_root; w ; w = w->next) {
1402                 if(w->target || (!w->processes && !w->exposed)) continue;
1403
1404                 fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + w->stime + w->fix_utime + w->fix_stime);
1405         }
1406         fprintf(stdout, "END\n");
1407
1408         fprintf(stdout, "BEGIN apps.cpu_user %llu\n", usec);
1409         for (w = target_root; w ; w = w->next) {
1410                 if(w->target || (!w->processes && !w->exposed)) continue;
1411
1412                 fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + w->fix_utime);
1413         }
1414         fprintf(stdout, "END\n");
1415
1416         fprintf(stdout, "BEGIN apps.cpu_system %llu\n", usec);
1417         for (w = target_root; w ; w = w->next) {
1418                 if(w->target || (!w->processes && !w->exposed)) continue;
1419
1420                 fprintf(stdout, "SET %s = %llu\n", w->name, w->stime + w->fix_stime);
1421         }
1422         fprintf(stdout, "END\n");
1423
1424         fprintf(stdout, "BEGIN apps.threads %llu\n", usec);
1425         for (w = target_root; w ; w = w->next) {
1426                 if(w->target || (!w->processes && !w->exposed)) continue;
1427
1428                 fprintf(stdout, "SET %s = %llu\n", w->name, w->num_threads);
1429         }
1430         fprintf(stdout, "END\n");
1431
1432         fprintf(stdout, "BEGIN apps.processes %llu\n", usec);
1433         for (w = target_root; w ; w = w->next) {
1434                 if(w->target || (!w->processes && !w->exposed)) continue;
1435
1436                 fprintf(stdout, "SET %s = %lu\n", w->name, w->processes);
1437         }
1438         fprintf(stdout, "END\n");
1439
1440         fprintf(stdout, "BEGIN apps.mem %llu\n", usec);
1441         for (w = target_root; w ; w = w->next) {
1442                 if(w->target || (!w->processes && !w->exposed)) continue;
1443
1444                 fprintf(stdout, "SET %s = %lld\n", w->name, (long long)w->statm_resident - (long long)w->statm_share);
1445         }
1446         fprintf(stdout, "END\n");
1447
1448         fprintf(stdout, "BEGIN apps.minor_faults %llu\n", usec);
1449         for (w = target_root; w ; w = w->next) {
1450                 if(w->target || (!w->processes && !w->exposed)) continue;
1451
1452                 fprintf(stdout, "SET %s = %llu\n", w->name, w->minflt + w->fix_minflt);
1453         }
1454         fprintf(stdout, "END\n");
1455
1456         fprintf(stdout, "BEGIN apps.major_faults %llu\n", usec);
1457         for (w = target_root; w ; w = w->next) {
1458                 if(w->target || (!w->processes && !w->exposed)) continue;
1459
1460                 fprintf(stdout, "SET %s = %llu\n", w->name, w->majflt + w->fix_majflt);
1461         }
1462         fprintf(stdout, "END\n");
1463
1464         fprintf(stdout, "BEGIN apps.lreads %llu\n", usec);
1465         for (w = target_root; w ; w = w->next) {
1466                 if(w->target || (!w->processes && !w->exposed)) continue;
1467
1468                 fprintf(stdout, "SET %s = %llu\n", w->name, w->io_logical_bytes_read);
1469         }
1470         fprintf(stdout, "END\n");
1471
1472         fprintf(stdout, "BEGIN apps.lwrites %llu\n", usec);
1473         for (w = target_root; w ; w = w->next) {
1474                 if(w->target || (!w->processes && !w->exposed)) continue;
1475
1476                 fprintf(stdout, "SET %s = %llu\n", w->name, w->io_logical_bytes_written);
1477         }
1478         fprintf(stdout, "END\n");
1479
1480         fprintf(stdout, "BEGIN apps.preads %llu\n", usec);
1481         for (w = target_root; w ; w = w->next) {
1482                 if(w->target || (!w->processes && !w->exposed)) continue;
1483
1484                 fprintf(stdout, "SET %s = %llu\n", w->name, w->io_storage_bytes_read);
1485         }
1486         fprintf(stdout, "END\n");
1487
1488         fprintf(stdout, "BEGIN apps.pwrites %llu\n", usec);
1489         for (w = target_root; w ; w = w->next) {
1490                 if(w->target || (!w->processes && !w->exposed)) continue;
1491
1492                 fprintf(stdout, "SET %s = %llu\n", w->name, w->io_storage_bytes_written);
1493         }
1494         fprintf(stdout, "END\n");
1495
1496         fprintf(stdout, "BEGIN apps.files %llu\n", usec);
1497         for (w = target_root; w ; w = w->next) {
1498                 if(w->target || (!w->processes && !w->exposed)) continue;
1499
1500                 fprintf(stdout, "SET %s = %llu\n", w->name, w->openfiles);
1501         }
1502         fprintf(stdout, "END\n");
1503
1504         fprintf(stdout, "BEGIN apps.sockets %llu\n", usec);
1505         for (w = target_root; w ; w = w->next) {
1506                 if(w->target || (!w->processes && !w->exposed)) continue;
1507
1508                 fprintf(stdout, "SET %s = %llu\n", w->name, w->opensockets);
1509         }
1510         fprintf(stdout, "END\n");
1511
1512         fprintf(stdout, "BEGIN apps.pipes %llu\n", usec);
1513         for (w = target_root; w ; w = w->next) {
1514                 if(w->target || (!w->processes && !w->exposed)) continue;
1515
1516                 fprintf(stdout, "SET %s = %llu\n", w->name, w->openpipes);
1517         }
1518         fprintf(stdout, "END\n");
1519
1520         fprintf(stdout, "BEGIN netdata.apps_cpu %llu\n", usec);
1521         fprintf(stdout, "SET user = %llu\n", cpuuser);
1522         fprintf(stdout, "SET system = %llu\n", cpusyst);
1523         fprintf(stdout, "END\n");
1524
1525         fprintf(stdout, "BEGIN netdata.apps_files %llu\n", usec);
1526         fprintf(stdout, "SET files = %llu\n", file_counter);
1527         fprintf(stdout, "SET pids = %ld\n", pids);
1528         fprintf(stdout, "SET fds = %d\n", all_files_len);
1529         fprintf(stdout, "SET targets = %ld\n", targets);
1530         fprintf(stdout, "END\n");
1531
1532         fflush(stdout);
1533 }
1534
1535
1536 // ----------------------------------------------------------------------------
1537 // generate the charts
1538
1539 void show_charts(void)
1540 {
1541         struct target *w;
1542         int newly_added = 0;
1543
1544         for(w = target_root ; w ; w = w->next)
1545                 if(!w->exposed && w->processes) {
1546                         newly_added++;
1547                         w->exposed = 1;
1548                         if(debug || w->debug) fprintf(stderr, "apps.plugin: %s just added - regenerating charts.\n", w->name);
1549                 }
1550
1551         // nothing more to show
1552         if(!newly_added) return;
1553
1554         // we have something new to show
1555         // update the charts
1556         fprintf(stdout, "CHART apps.cpu '' 'Apps CPU Time (%ld%% = %ld core%s)' 'cpu time %%' apps apps stacked 20001 %d\n", (processors * 100), processors, (processors>1)?"s":"", update_every);
1557         for (w = target_root; w ; w = w->next) {
1558                 if(w->target || (!w->processes && !w->exposed)) continue;
1559
1560                 fprintf(stdout, "DIMENSION %s '' incremental 100 %llu %s\n", w->name, (unsigned long long)(Hertz * update_every), w->hidden?"hidden":"");
1561         }
1562
1563         fprintf(stdout, "CHART apps.mem '' 'Apps Dedicated Memory (w/o shared)' 'MB' apps apps stacked 20003 %d\n", update_every);
1564         for (w = target_root; w ; w = w->next) {
1565                 if(w->target || (!w->processes && !w->exposed)) continue;
1566
1567                 fprintf(stdout, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L);
1568         }
1569
1570         fprintf(stdout, "CHART apps.threads '' 'Apps Threads' 'threads' apps apps stacked 20005 %d\n", update_every);
1571         for (w = target_root; w ; w = w->next) {
1572                 if(w->target || (!w->processes && !w->exposed)) continue;
1573
1574                 fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
1575         }
1576
1577         fprintf(stdout, "CHART apps.processes '' 'Apps Processes' 'processes' apps apps stacked 20004 %d\n", update_every);
1578         for (w = target_root; w ; w = w->next) {
1579                 if(w->target || (!w->processes && !w->exposed)) continue;
1580
1581                 fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
1582         }
1583
1584         fprintf(stdout, "CHART apps.cpu_user '' 'Apps CPU User Time (%ld%% = %ld core%s)' 'cpu time %%' apps none stacked 20020 %d\n", (processors * 100), processors, (processors>1)?"s":"", update_every);
1585         for (w = target_root; w ; w = w->next) {
1586                 if(w->target || (!w->processes && !w->exposed)) continue;
1587
1588                 fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz * processors * update_every);
1589         }
1590
1591         fprintf(stdout, "CHART apps.cpu_system '' 'Apps CPU System Time (%ld%% = %ld core%s)' 'cpu time %%' apps none stacked 20021 %d\n", (processors * 100), processors, (processors>1)?"s":"", update_every);
1592         for (w = target_root; w ; w = w->next) {
1593                 if(w->target || (!w->processes && !w->exposed)) continue;
1594
1595                 fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz * processors * update_every);
1596         }
1597
1598         fprintf(stdout, "CHART apps.major_faults '' 'Apps Major Page Faults (swaps in)' 'page faults/s' apps apps stacked 20010 %d\n", update_every);
1599         for (w = target_root; w ; w = w->next) {
1600                 if(w->target || (!w->processes && !w->exposed)) continue;
1601
1602                 fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, update_every);
1603         }
1604
1605         fprintf(stdout, "CHART apps.minor_faults '' 'Apps Minor Page Faults' 'page faults/s' apps none stacked 20011 %d\n", update_every);
1606         for (w = target_root; w ; w = w->next) {
1607                 if(w->target || (!w->processes && !w->exposed)) continue;
1608
1609                 fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, update_every);
1610         }
1611
1612         fprintf(stdout, "CHART apps.lreads '' 'Apps Disk Logical Reads' 'kilobytes/s' apps none stacked 20042 %d\n", update_every);
1613         for (w = target_root; w ; w = w->next) {
1614                 if(w->target || (!w->processes && !w->exposed)) continue;
1615
1616                 fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, 1024 * update_every);
1617         }
1618
1619         fprintf(stdout, "CHART apps.lwrites '' 'Apps I/O Logical Writes' 'kilobytes/s' apps none stacked 20042 %d\n", update_every);
1620         for (w = target_root; w ; w = w->next) {
1621                 if(w->target || (!w->processes && !w->exposed)) continue;
1622
1623                 fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, 1024 * update_every);
1624         }
1625
1626         fprintf(stdout, "CHART apps.preads '' 'Apps Disk Reads' 'kilobytes/s' apps apps stacked 20002 %d\n", update_every);
1627         for (w = target_root; w ; w = w->next) {
1628                 if(w->target || (!w->processes && !w->exposed)) continue;
1629
1630                 fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, 1024 * update_every);
1631         }
1632
1633         fprintf(stdout, "CHART apps.pwrites '' 'Apps Disk Writes' 'kilobytes/s' apps apps stacked 20002 %d\n", update_every);
1634         for (w = target_root; w ; w = w->next) {
1635                 if(w->target || (!w->processes && !w->exposed)) continue;
1636
1637                 fprintf(stdout, "DIMENSION %s '' incremental 1 %d\n", w->name, 1024 * update_every);
1638         }
1639
1640         fprintf(stdout, "CHART apps.files '' 'Apps Open Files' 'open files' apps apps stacked 20050 %d\n", update_every);
1641         for (w = target_root; w ; w = w->next) {
1642                 if(w->target || (!w->processes && !w->exposed)) continue;
1643
1644                 fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
1645         }
1646
1647         fprintf(stdout, "CHART apps.sockets '' 'Apps Open Sockets' 'open sockets' apps apps stacked 20051 %d\n", update_every);
1648         for (w = target_root; w ; w = w->next) {
1649                 if(w->target || (!w->processes && !w->exposed)) continue;
1650
1651                 fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
1652         }
1653
1654         fprintf(stdout, "CHART apps.pipes '' 'Apps Pipes' 'open pipes' apps none stacked 20053 %d\n", update_every);
1655         for (w = target_root; w ; w = w->next) {
1656                 if(w->target || (!w->processes && !w->exposed)) continue;
1657
1658                 fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);
1659         }
1660
1661         fprintf(stdout, "CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' netdata netdata stacked 10000 %d\n", update_every);
1662         fprintf(stdout, "DIMENSION user '' incremental 1 %d\n", 1000 * update_every);
1663         fprintf(stdout, "DIMENSION system '' incremental 1 %d\n", 1000 * update_every);
1664
1665         fprintf(stdout, "CHART netdata.apps_files '' 'Apps Plugin Files' 'files/s' netdata netdata line 10001 %d\n", update_every);
1666         fprintf(stdout, "DIMENSION files '' incremental 1 %d\n", update_every);
1667         fprintf(stdout, "DIMENSION pids '' absolute 1 1\n");
1668         fprintf(stdout, "DIMENSION fds '' absolute 1 1\n");
1669         fprintf(stdout, "DIMENSION targets '' absolute 1 1\n");
1670
1671         fflush(stdout);
1672 }
1673
1674
1675 // ----------------------------------------------------------------------------
1676 // parse command line arguments
1677
1678 void parse_args(int argc, char **argv)
1679 {
1680         int i, freq = 0;
1681         char *name = NULL;
1682
1683         for(i = 1; i < argc; i++) {
1684                 if(!freq) {
1685                         int n = atoi(argv[i]);
1686                         if(n > 0) {
1687                                 freq = n;
1688                                 continue;
1689                         }
1690                 }
1691
1692                 if(strcmp("debug", argv[i]) == 0) {
1693                         debug = 1;
1694                         continue;
1695                 }
1696
1697                 if(!name) {
1698                         name = argv[i];
1699                         continue;
1700                 }
1701
1702                 error("apps.plugin: ERROR: cannot understand option %s", argv[i]);
1703                 exit(1);
1704         }
1705
1706         if(freq > 0) update_every = freq;
1707         if(!name) name = "groups";
1708
1709         if(read_process_groups(name)) {
1710                 error("apps.plugin: ERROR: cannot read process groups %s", name);
1711                 exit(1);
1712         }
1713 }
1714
1715 int main(int argc, char **argv)
1716 {
1717         // debug_flags = D_PROCFILE;
1718
1719         info("apps.plugin: starting...");
1720
1721         unsigned long started_t = time(NULL), current_t;
1722         Hertz = get_hertz();
1723         pid_max = get_pid_max();
1724         processors = get_processors();
1725
1726         parse_args(argc, argv);
1727
1728         all_pids = calloc(sizeof(struct pid_stat *), pid_max);
1729         if(!all_pids) {
1730                 error("apps.plugin: ERROR: cannot allocate %lu bytes of memory.", sizeof(struct pid_stat *) * pid_max);
1731                 printf("DISABLE\n");
1732                 exit(1);
1733         }
1734
1735         unsigned long long counter = 1;
1736         unsigned long long usec = 0, susec = 0;
1737         struct timeval last, now;
1738         gettimeofday(&last, NULL);
1739
1740         for(;1; counter++) {
1741                 if(!update_from_proc()) {
1742                         error("apps.plugin: ERROR: cannot allocate %lu bytes of memory.", sizeof(struct pid_stat *) * pid_max);
1743                         printf("DISABLE\n");
1744                         exit(1);
1745                 }
1746
1747                 update_statistics();
1748                 show_charts();          // this is smart enough to show only newly added apps, when needed
1749                 show_dimensions();
1750
1751                 if(debug) fprintf(stderr, "apps.plugin: done Loop No %llu\n", counter);
1752                 fflush(NULL);
1753
1754                 gettimeofday(&now, NULL);
1755                 usec = usecdiff(&now, &last) - susec;
1756                 if(debug) fprintf(stderr, "apps.plugin: last loop took %llu usec (worked for %llu, sleeped for %llu).\n", usec + susec, usec, susec);
1757
1758                 // if the last loop took less than half the time
1759                 // wait the rest of the time
1760                 if(usec < (update_every * 1000000ULL / 2)) susec = (update_every * 1000000ULL) - usec;
1761                 else susec = update_every * 1000000ULL / 2;
1762
1763                 usleep(susec);
1764                 bcopy(&now, &last, sizeof(struct timeval));
1765                 
1766                 current_t = time(NULL);
1767                 if(current_t - started_t > 3600) exit(0);
1768         }
1769 }