X-Git-Url: https://arthur.barton.de/gitweb/?a=blobdiff_plain;f=src%2Fapps_plugin.c;h=82b77df655ff5a945c1b5e327f80adfd9fb24b97;hb=ce381953ca239d092b3085ee29798a4908f32747;hp=b69171395085b375bf39530c920677ccc3881e4c;hpb=2785e60bf830254c6683ab033e4d080167df1ef0;p=netdata.git diff --git a/src/apps_plugin.c b/src/apps_plugin.c index b6917139..82b77df6 100644 --- a/src/apps_plugin.c +++ b/src/apps_plugin.c @@ -7,6 +7,27 @@ #include "common.h" +#ifdef __FreeBSD__ +#include +#endif + +// ---------------------------------------------------------------------------- +// per O/S configuration + +// the minimum PID of the system +// this is also the pid of the init process +#define INIT_PID 1 + +// if the way apps.plugin will work, will read the entire process list, +// including the resource utilization of each process, instantly +// set this to 1 +// when set to 0, apps.plugin builds a sort list of processes, in order +// to process children processes, before parent processes +#ifdef __FreeBSD__ +#define ALL_PIDS_ARE_READ_INSTANTLY 1 +#else +#define ALL_PIDS_ARE_READ_INSTANTLY 0 +#endif // ---------------------------------------------------------------------------- // string lengths @@ -64,6 +85,7 @@ static int static size_t global_iterations_counter = 1, + calls_counter = 0, file_counter = 0; @@ -293,7 +315,6 @@ struct pid_stat { int keeploops; // increases by 1 every time keep is 1 and updated 0 char updated:1; // 1 when the process is currently running char merged:1; // 1 when it has been merged to its parent - char new_entry:1; // 1 when this is a new process, just saw for the first time char read:1; // 1 when we have already read this process for this iteration int sortlist; // higher numbers = top on the process tree @@ -337,6 +358,7 @@ static struct pid_stat static size_t all_pids_count = 0; // the number of processes running +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0) // Another pre-allocated list of all possible pids. // We need it to pids and assign them a unique sortlist id, so that we // read parents before children. This is needed to prevent a situation where @@ -344,7 +366,7 @@ static size_t // its parent has accumulated its resources. static pid_t *all_pids_sortlist = NULL; - +#endif // ---------------------------------------------------------------------------- // file descriptor @@ -354,6 +376,19 @@ static pid_t #define FILE_DESCRIPTORS_INCREASE_STEP 100 +// types for struct file_descriptor->type +typedef enum fd_filetype { + FILETYPE_OTHER, + FILETYPE_FILE, + FILETYPE_PIPE, + FILETYPE_SOCKET, + FILETYPE_INOTIFY, + FILETYPE_EVENTFD, + FILETYPE_EVENTPOLL, + FILETYPE_TIMERFD, + FILETYPE_SIGNALFD +} FD_FILETYPE; + struct file_descriptor { avl avl; @@ -364,7 +399,7 @@ struct file_descriptor { const char *name; uint32_t hash; - char type; + FD_FILETYPE type; int count; int pos; } *all_files = NULL; @@ -373,18 +408,6 @@ static int all_files_len = 0, all_files_size = 0; -// types for struct file_descriptor->type -#define FILETYPE_OTHER 0 -#define FILETYPE_FILE 1 -#define FILETYPE_PIPE 2 -#define FILETYPE_SOCKET 3 -#define FILETYPE_INOTIFY 4 -#define FILETYPE_EVENTFD 5 -#define FILETYPE_EVENTPOLL 6 -#define FILETYPE_TIMERFD 7 -#define FILETYPE_SIGNALFD 8 - - // ---------------------------------------------------------------------------- // callback required by fatal() @@ -626,65 +649,152 @@ static int read_apps_groups_conf(const char *file) // struct pid_stat management static inline struct pid_stat *get_pid_entry(pid_t pid) { - if(unlikely(all_pids[pid])) { - all_pids[pid]->new_entry = 0; + if(unlikely(all_pids[pid])) return all_pids[pid]; - } - all_pids[pid] = callocz(sizeof(struct pid_stat), 1); - all_pids[pid]->fds = callocz(sizeof(int), MAX_SPARE_FDS); - all_pids[pid]->fds_size = MAX_SPARE_FDS; + struct pid_stat *p = callocz(sizeof(struct pid_stat), 1); + p->fds = callocz(sizeof(int), MAX_SPARE_FDS); + p->fds_size = MAX_SPARE_FDS; if(likely(root_of_pids)) - root_of_pids->prev = all_pids[pid]; + root_of_pids->prev = p; - all_pids[pid]->next = root_of_pids; - root_of_pids = all_pids[pid]; + p->next = root_of_pids; + root_of_pids = p; - all_pids[pid]->pid = pid; - all_pids[pid]->new_entry = 1; + p->pid = pid; + all_pids[pid] = p; all_pids_count++; - return all_pids[pid]; + return p; } static inline void del_pid_entry(pid_t pid) { - if(unlikely(!all_pids[pid])) { + struct pid_stat *p = all_pids[pid]; + + if(unlikely(!p)) { error("attempted to free pid %d that is not allocated.", pid); return; } if(unlikely(debug)) - fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, all_pids[pid]->comm); + fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, p->comm); - if(root_of_pids == all_pids[pid]) - root_of_pids = all_pids[pid]->next; + if(root_of_pids == p) + root_of_pids = p->next; - if(all_pids[pid]->next) all_pids[pid]->next->prev = all_pids[pid]->prev; - if(all_pids[pid]->prev) all_pids[pid]->prev->next = all_pids[pid]->next; + if(p->next) p->next->prev = p->prev; + if(p->prev) p->prev->next = p->next; - freez(all_pids[pid]->fds); - freez(all_pids[pid]->fds_dirname); - freez(all_pids[pid]->stat_filename); - freez(all_pids[pid]->statm_filename); - freez(all_pids[pid]->io_filename); - freez(all_pids[pid]->cmdline_filename); - freez(all_pids[pid]); + freez(p->fds); + freez(p->fds_dirname); + freez(p->stat_filename); + freez(p->statm_filename); + freez(p->io_filename); + freez(p->cmdline_filename); + freez(p); all_pids[pid] = NULL; all_pids_count--; } +// ---------------------------------------------------------------------------- + +static inline int managed_log(struct pid_stat *p, uint32_t log, int status) { + if(unlikely(!status)) { + // error("command failed log %u, errno %d", log, errno); + + if(unlikely(debug || errno != ENOENT)) { + if(unlikely(debug || !(p->log_thrown & log))) { + p->log_thrown |= log; + switch(log) { + case PID_LOG_IO: + error("Cannot process %s/proc/%d/io (command '%s')", netdata_configured_host_prefix, p->pid, p->comm); + break; + + case PID_LOG_STATM: + error("Cannot process %s/proc/%d/statm (command '%s')", netdata_configured_host_prefix, p->pid, p->comm); + break; + + case PID_LOG_CMDLINE: + error("Cannot process %s/proc/%d/cmdline (command '%s')", netdata_configured_host_prefix, p->pid, p->comm); + break; + + case PID_LOG_FDS: + error("Cannot process entries in %s/proc/%d/fd (command '%s')", netdata_configured_host_prefix, p->pid, p->comm); + break; + + case PID_LOG_STAT: + break; + + default: + error("unhandled error for pid %d, command '%s'", p->pid, p->comm); + break; + } + } + } + errno = 0; + } + else if(unlikely(p->log_thrown & log)) { + // error("unsetting log %u on pid %d", log, p->pid); + p->log_thrown &= ~log; + } + + return status; +} + +static inline void assign_target_to_pid(struct pid_stat *p) { + uint32_t hash = simple_hash(p->comm); + size_t pclen = strlen(p->comm); + + struct target *w; + for(w = apps_groups_root_target; w ; w = w->next) { + // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm); + + // find it - 4 cases: + // 1. the target is not a pattern + // 2. the target has the prefix + // 3. the target has the suffix + // 4. the target is something inside cmdline + + if(unlikely(( (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm)) + || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen)) + || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen])) + || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && strstr(p->cmdline, w->compare)) + ))) { + + if(w->target) p->target = w->target; + else p->target = w; + + if(debug || (p->target && p->target->debug)) + fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name); + + break; + } + } +} + // ---------------------------------------------------------------------------- // update pids from proc static inline int read_proc_pid_cmdline(struct pid_stat *p) { +#ifdef __FreeBSD__ + size_t i, bytes = MAX_CMDLINE; + int mib[4]; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_ARGS; + mib[3] = p->pid; + if (unlikely(sysctl(mib, 4, p->cmdline, &bytes, NULL, 0))) + goto cleanup; +#else if(unlikely(!p->cmdline_filename)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", global_host_prefix, p->pid); + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/cmdline", netdata_configured_host_prefix, p->pid); p->cmdline_filename = strdupz(filename); } @@ -695,6 +805,7 @@ static inline int read_proc_pid_cmdline(struct pid_stat *p) { close(fd); if(unlikely(bytes < 0)) goto cleanup; +#endif p->cmdline[bytes] = '\0'; for(i = 0; i < bytes ; i++) @@ -711,7 +822,16 @@ cleanup: return 0; } -static inline int read_proc_pid_ownership(struct pid_stat *p) { +static inline int read_proc_pid_ownership(struct pid_stat *p, void *ptr) { + (void)ptr; +#ifdef __FreeBSD__ + struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr; + + p->uid = proc_info->ki_uid; + p->gid = proc_info->ki_groups[0]; + + return 1; +#else if(unlikely(!p->stat_filename)) { error("pid %d does not have a stat_filename", p->pid); return 0; @@ -730,14 +850,41 @@ static inline int read_proc_pid_ownership(struct pid_stat *p) { p->gid = st.st_gid; return 1; +#endif } -static inline int read_proc_pid_stat(struct pid_stat *p) { +// ---------------------------------------------------------------------------- +// macro to calculate the incremental rate of a value +// each parameter is accessed only ONCE - so it is safe to pass function calls +// or other macros as parameters + +#define incremental_rate(rate_variable, last_kernel_variable, new_kernel_value, collected_usec, last_collected_usec) { \ + kernel_uint_t _new_tmp = new_kernel_value; \ + rate_variable = (_new_tmp - last_kernel_variable) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); \ + last_kernel_variable = _new_tmp; \ + } + +// the same macro for struct pid members +#define pid_incremental_rate(type, var, value) \ + incremental_rate(var, var##_raw, value, p->type##_collected_usec, p->last_##type##_collected_usec) + + +// ---------------------------------------------------------------------------- + +static inline int read_proc_pid_stat(struct pid_stat *p, void *ptr) { + (void)ptr; + +#ifdef __FreeBSD__ + struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr; + + if (unlikely(proc_info->ki_tdflags & TDF_IDLETD)) + goto cleanup; +#else static procfile *ff = NULL; if(unlikely(!p->stat_filename)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", global_host_prefix, p->pid); + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/stat", netdata_configured_host_prefix, p->pid); p->stat_filename = strdupz(filename); } @@ -752,92 +899,99 @@ static inline int read_proc_pid_stat(struct pid_stat *p) { ff = procfile_readall(ff); if(unlikely(!ff)) goto cleanup; +#endif p->last_stat_collected_usec = p->stat_collected_usec; - p->stat_collected_usec = now_realtime_usec(); - file_counter++; - - // p->pid = str2pid_t(procfile_lineword(ff, 0, 0+i)); - - if(unlikely(!p->comm[0])) - strncpyz(p->comm, procfile_lineword(ff, 0, 1), MAX_COMPARE_NAME); + p->stat_collected_usec = now_monotonic_usec(); + calls_counter++; +#ifdef __FreeBSD__ + char *comm = proc_info->ki_comm; + p->ppid = proc_info->ki_ppid; +#else + // p->pid = str2pid_t(procfile_lineword(ff, 0, 0)); + char *comm = procfile_lineword(ff, 0, 1); // p->state = *(procfile_lineword(ff, 0, 2)); p->ppid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 3)); - // p->pgrp = str2ul(procfile_lineword(ff, 0, 4)); - // p->session = str2ul(procfile_lineword(ff, 0, 5)); - // p->tty_nr = str2ul(procfile_lineword(ff, 0, 6)); - // p->tpgid = str2ul(procfile_lineword(ff, 0, 7)); - // p->flags = str2ull(procfile_lineword(ff, 0, 8)); - - kernel_uint_t last; - - last = p->minflt_raw; - p->minflt_raw = str2kernel_unit_t(procfile_lineword(ff, 0, 9)); - p->minflt = (p->minflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); - - last = p->cminflt_raw; - p->cminflt_raw = str2kernel_unit_t(procfile_lineword(ff, 0, 10)); - p->cminflt = (p->cminflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + // p->pgrp = (int32_t)str2pid_t(procfile_lineword(ff, 0, 4)); + // p->session = (int32_t)str2pid_t(procfile_lineword(ff, 0, 5)); + // p->tty_nr = (int32_t)str2pid_t(procfile_lineword(ff, 0, 6)); + // p->tpgid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 7)); + // p->flags = str2uint64_t(procfile_lineword(ff, 0, 8)); +#endif - last = p->majflt_raw; - p->majflt_raw = str2kernel_unit_t(procfile_lineword(ff, 0, 11)); - p->majflt = (p->majflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + if(strcmp(p->comm, comm)) { + if(unlikely(debug)) { + if(p->comm[0]) + fprintf(stderr, "apps.plugin: \tpid %d (%s) changed name to '%s'\n", p->pid, p->comm, comm); + else + fprintf(stderr, "apps.plugin: \tJust added %d (%s)\n", p->pid, comm); + } - last = p->cmajflt_raw; - p->cmajflt_raw = str2kernel_unit_t(procfile_lineword(ff, 0, 12)); - p->cmajflt = (p->cmajflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + strncpyz(p->comm, comm, MAX_COMPARE_NAME); - last = p->utime_raw; - p->utime_raw = str2kernel_unit_t(procfile_lineword(ff, 0, 13)); - p->utime = (p->utime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + // /proc//cmdline + if(likely(proc_pid_cmdline_is_needed)) + managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p)); - last = p->stime_raw; - p->stime_raw = str2kernel_unit_t(procfile_lineword(ff, 0, 14)); - p->stime = (p->stime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + assign_target_to_pid(p); + } - last = p->cutime_raw; - p->cutime_raw = str2kernel_unit_t(procfile_lineword(ff, 0, 15)); - p->cutime = (p->cutime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); +#ifdef __FreeBSD__ + pid_incremental_rate(stat, p->minflt, (kernel_uint_t)proc_info->ki_rusage.ru_minflt); + pid_incremental_rate(stat, p->cminflt, (kernel_uint_t)proc_info->ki_rusage_ch.ru_minflt); + pid_incremental_rate(stat, p->majflt, (kernel_uint_t)proc_info->ki_rusage.ru_majflt); + pid_incremental_rate(stat, p->cmajflt, (kernel_uint_t)proc_info->ki_rusage_ch.ru_majflt); + pid_incremental_rate(stat, p->utime, (kernel_uint_t)proc_info->ki_rusage.ru_utime.tv_sec * 100 + proc_info->ki_rusage.ru_utime.tv_usec / 10000); + pid_incremental_rate(stat, p->stime, (kernel_uint_t)proc_info->ki_rusage.ru_stime.tv_sec * 100 + proc_info->ki_rusage.ru_stime.tv_usec / 10000); + pid_incremental_rate(stat, p->cutime, (kernel_uint_t)proc_info->ki_rusage_ch.ru_utime.tv_sec * 100 + proc_info->ki_rusage_ch.ru_utime.tv_usec / 10000); + pid_incremental_rate(stat, p->cstime, (kernel_uint_t)proc_info->ki_rusage_ch.ru_stime.tv_sec * 100 + proc_info->ki_rusage_ch.ru_utime.tv_usec / 10000); - last = p->cstime_raw; - p->cstime_raw = str2kernel_unit_t(procfile_lineword(ff, 0, 16)); - p->cstime = (p->cstime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + p->num_threads = proc_info->ki_numthreads; - // p->priority = str2kernel_unit_t(procfile_lineword(ff, 0, 17)); - // p->nice = str2kernel_unit_t(procfile_lineword(ff, 0, 18)); + if(enable_guest_charts) { + enable_guest_charts = 0; + info("Guest charts aren't supported by FreeBSD"); + } +#else + pid_incremental_rate(stat, p->minflt, str2kernel_uint_t(procfile_lineword(ff, 0, 9))); + pid_incremental_rate(stat, p->cminflt, str2kernel_uint_t(procfile_lineword(ff, 0, 10))); + pid_incremental_rate(stat, p->majflt, str2kernel_uint_t(procfile_lineword(ff, 0, 11))); + pid_incremental_rate(stat, p->cmajflt, str2kernel_uint_t(procfile_lineword(ff, 0, 12))); + pid_incremental_rate(stat, p->utime, str2kernel_uint_t(procfile_lineword(ff, 0, 13))); + pid_incremental_rate(stat, p->stime, str2kernel_uint_t(procfile_lineword(ff, 0, 14))); + pid_incremental_rate(stat, p->cutime, str2kernel_uint_t(procfile_lineword(ff, 0, 15))); + pid_incremental_rate(stat, p->cstime, str2kernel_uint_t(procfile_lineword(ff, 0, 16))); + // p->priority = str2kernel_uint_t(procfile_lineword(ff, 0, 17)); + // p->nice = str2kernel_uint_t(procfile_lineword(ff, 0, 18)); p->num_threads = (int32_t)str2uint32_t(procfile_lineword(ff, 0, 19)); - // p->itrealvalue = str2kernel_unit_t(procfile_lineword(ff, 0, 20)); - // p->starttime = str2kernel_unit_t(procfile_lineword(ff, 0, 21)); - // p->vsize = str2kernel_unit_t(procfile_lineword(ff, 0, 22)); - // p->rss = str2kernel_unit_t(procfile_lineword(ff, 0, 23)); - // p->rsslim = str2kernel_unit_t(procfile_lineword(ff, 0, 24)); - // p->starcode = str2kernel_unit_t(procfile_lineword(ff, 0, 25)); - // p->endcode = str2kernel_unit_t(procfile_lineword(ff, 0, 26)); - // p->startstack = str2kernel_unit_t(procfile_lineword(ff, 0, 27)); - // p->kstkesp = str2kernel_unit_t(procfile_lineword(ff, 0, 28)); - // p->kstkeip = str2kernel_unit_t(procfile_lineword(ff, 0, 29)); - // p->signal = str2kernel_unit_t(procfile_lineword(ff, 0, 30)); - // p->blocked = str2kernel_unit_t(procfile_lineword(ff, 0, 31)); - // p->sigignore = str2kernel_unit_t(procfile_lineword(ff, 0, 32)); - // p->sigcatch = str2kernel_unit_t(procfile_lineword(ff, 0, 33)); - // p->wchan = str2kernel_unit_t(procfile_lineword(ff, 0, 34)); - // p->nswap = str2kernel_unit_t(procfile_lineword(ff, 0, 35)); - // p->cnswap = str2kernel_unit_t(procfile_lineword(ff, 0, 36)); - // p->exit_signal = str2kernel_unit_t(procfile_lineword(ff, 0, 37)); - // p->processor = str2kernel_unit_t(procfile_lineword(ff, 0, 38)); - // p->rt_priority = str2kernel_unit_t(procfile_lineword(ff, 0, 39)); - // p->policy = str2kernel_unit_t(procfile_lineword(ff, 0, 40)); - // p->delayacct_blkio_ticks = str2kernel_unit_t(procfile_lineword(ff, 0, 41)); + // p->itrealvalue = str2kernel_uint_t(procfile_lineword(ff, 0, 20)); + // p->starttime = str2kernel_uint_t(procfile_lineword(ff, 0, 21)); + // p->vsize = str2kernel_uint_t(procfile_lineword(ff, 0, 22)); + // p->rss = str2kernel_uint_t(procfile_lineword(ff, 0, 23)); + // p->rsslim = str2kernel_uint_t(procfile_lineword(ff, 0, 24)); + // p->starcode = str2kernel_uint_t(procfile_lineword(ff, 0, 25)); + // p->endcode = str2kernel_uint_t(procfile_lineword(ff, 0, 26)); + // p->startstack = str2kernel_uint_t(procfile_lineword(ff, 0, 27)); + // p->kstkesp = str2kernel_uint_t(procfile_lineword(ff, 0, 28)); + // p->kstkeip = str2kernel_uint_t(procfile_lineword(ff, 0, 29)); + // p->signal = str2kernel_uint_t(procfile_lineword(ff, 0, 30)); + // p->blocked = str2kernel_uint_t(procfile_lineword(ff, 0, 31)); + // p->sigignore = str2kernel_uint_t(procfile_lineword(ff, 0, 32)); + // p->sigcatch = str2kernel_uint_t(procfile_lineword(ff, 0, 33)); + // p->wchan = str2kernel_uint_t(procfile_lineword(ff, 0, 34)); + // p->nswap = str2kernel_uint_t(procfile_lineword(ff, 0, 35)); + // p->cnswap = str2kernel_uint_t(procfile_lineword(ff, 0, 36)); + // p->exit_signal = str2kernel_uint_t(procfile_lineword(ff, 0, 37)); + // p->processor = str2kernel_uint_t(procfile_lineword(ff, 0, 38)); + // p->rt_priority = str2kernel_uint_t(procfile_lineword(ff, 0, 39)); + // p->policy = str2kernel_uint_t(procfile_lineword(ff, 0, 40)); + // p->delayacct_blkio_ticks = str2kernel_uint_t(procfile_lineword(ff, 0, 41)); if(enable_guest_charts) { - last = p->gtime_raw; - p->gtime_raw = str2kernel_unit_t(procfile_lineword(ff, 0, 42)); - p->gtime = (p->gtime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); - last = p->cgtime_raw; - p->cgtime_raw = str2kernel_unit_t(procfile_lineword(ff, 0, 43)); - p->cgtime = (p->cgtime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + pid_incremental_rate(stat, p->gtime, str2kernel_uint_t(procfile_lineword(ff, 0, 42))); + pid_incremental_rate(stat, p->cgtime, str2kernel_uint_t(procfile_lineword(ff, 0, 43))); if (show_guest_time || p->gtime || p->cgtime) { p->utime -= (p->utime >= p->gtime) ? p->gtime : p->utime; @@ -845,9 +999,10 @@ static inline int read_proc_pid_stat(struct pid_stat *p) { show_guest_time = 1; } } +#endif if(unlikely(debug || (p->target && p->target->debug))) - fprintf(stderr, "apps.plugin: READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT ", threads=%d\n", global_host_prefix, p->pid, p->comm, (p->target)?p->target->name:"UNSET", p->stat_collected_usec - p->last_stat_collected_usec, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads); + fprintf(stderr, "apps.plugin: READ PROC/PID/STAT: %s/proc/%d/stat, process: '%s' on target '%s' (dt=%llu) VALUES: utime=" KERNEL_UINT_FORMAT ", stime=" KERNEL_UINT_FORMAT ", cutime=" KERNEL_UINT_FORMAT ", cstime=" KERNEL_UINT_FORMAT ", minflt=" KERNEL_UINT_FORMAT ", majflt=" KERNEL_UINT_FORMAT ", cminflt=" KERNEL_UINT_FORMAT ", cmajflt=" KERNEL_UINT_FORMAT ", threads=%d\n", netdata_configured_host_prefix, p->pid, p->comm, (p->target)?p->target->name:"UNSET", p->stat_collected_usec - p->last_stat_collected_usec, p->utime, p->stime, p->cutime, p->cstime, p->minflt, p->majflt, p->cminflt, p->cmajflt, p->num_threads); if(unlikely(global_iterations_counter == 1)) { p->minflt = 0; @@ -880,12 +1035,16 @@ cleanup: return 0; } -static inline int read_proc_pid_statm(struct pid_stat *p) { +static inline int read_proc_pid_statm(struct pid_stat *p, void *ptr) { + (void)ptr; +#ifdef __FreeBSD__ + struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr; +#else static procfile *ff = NULL; if(unlikely(!p->statm_filename)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/statm", global_host_prefix, p->pid); + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/statm", netdata_configured_host_prefix, p->pid); p->statm_filename = strdupz(filename); } @@ -894,16 +1053,23 @@ static inline int read_proc_pid_statm(struct pid_stat *p) { ff = procfile_readall(ff); if(unlikely(!ff)) goto cleanup; +#endif - file_counter++; + calls_counter++; - p->statm_size = str2ull(procfile_lineword(ff, 0, 0)); - p->statm_resident = str2ull(procfile_lineword(ff, 0, 1)); - p->statm_share = str2ull(procfile_lineword(ff, 0, 2)); - // p->statm_text = str2ull(procfile_lineword(ff, 0, 3)); - // p->statm_lib = str2ull(procfile_lineword(ff, 0, 4)); - // p->statm_data = str2ull(procfile_lineword(ff, 0, 5)); - // p->statm_dirty = str2ull(procfile_lineword(ff, 0, 6)); +#ifdef __FreeBSD__ + p->statm_size = proc_info->ki_size / sysconf(_SC_PAGESIZE); + p->statm_resident = proc_info->ki_rssize; + p->statm_share = 0; // do we have to use ru_ixrss here? +#else + p->statm_size = str2kernel_uint_t(procfile_lineword(ff, 0, 0)); + p->statm_resident = str2kernel_uint_t(procfile_lineword(ff, 0, 1)); + p->statm_share = str2kernel_uint_t(procfile_lineword(ff, 0, 2)); + // p->statm_text = str2kernel_uint_t(procfile_lineword(ff, 0, 3)); + // p->statm_lib = str2kernel_uint_t(procfile_lineword(ff, 0, 4)); + // p->statm_data = str2kernel_uint_t(procfile_lineword(ff, 0, 5)); + // p->statm_dirty = str2kernel_uint_t(procfile_lineword(ff, 0, 6)); +#endif return 1; @@ -918,12 +1084,16 @@ cleanup: return 0; } -static inline int read_proc_pid_io(struct pid_stat *p) { +static inline int read_proc_pid_io(struct pid_stat *p, void *ptr) { + (void)ptr; +#ifdef __FreeBSD__ + struct kinfo_proc *proc_info = (struct kinfo_proc *)ptr; +#else static procfile *ff = NULL; if(unlikely(!p->io_filename)) { char filename[FILENAME_MAX + 1]; - snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", global_host_prefix, p->pid); + snprintfz(filename, FILENAME_MAX, "%s/proc/%d/io", netdata_configured_host_prefix, p->pid); p->io_filename = strdupz(filename); } @@ -933,41 +1103,25 @@ static inline int read_proc_pid_io(struct pid_stat *p) { ff = procfile_readall(ff); if(unlikely(!ff)) goto cleanup; +#endif - file_counter++; + calls_counter++; p->last_io_collected_usec = p->io_collected_usec; - p->io_collected_usec = now_realtime_usec(); - - kernel_uint_t last; - - last = p->io_logical_bytes_read_raw; - p->io_logical_bytes_read_raw = str2ull(procfile_lineword(ff, 0, 1)); - p->io_logical_bytes_read = (p->io_logical_bytes_read_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); - - last = p->io_logical_bytes_written_raw; - p->io_logical_bytes_written_raw = str2ull(procfile_lineword(ff, 1, 1)); - p->io_logical_bytes_written = (p->io_logical_bytes_written_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); - - // last = p->io_read_calls_raw; - // p->io_read_calls_raw = str2ull(procfile_lineword(ff, 2, 1)); - // p->io_read_calls = (p->io_read_calls_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); - - // last = p->io_write_calls_raw; - // p->io_write_calls_raw = str2ull(procfile_lineword(ff, 3, 1)); - // p->io_write_calls = (p->io_write_calls_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); + p->io_collected_usec = now_monotonic_usec(); - last = p->io_storage_bytes_read_raw; - p->io_storage_bytes_read_raw = str2ull(procfile_lineword(ff, 4, 1)); - p->io_storage_bytes_read = (p->io_storage_bytes_read_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); - - last = p->io_storage_bytes_written_raw; - p->io_storage_bytes_written_raw = str2ull(procfile_lineword(ff, 5, 1)); - p->io_storage_bytes_written = (p->io_storage_bytes_written_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); - - // last = p->io_cancelled_write_bytes_raw; - // p->io_cancelled_write_bytes_raw = str2ull(procfile_lineword(ff, 6, 1)); - // p->io_cancelled_write_bytes = (p->io_cancelled_write_bytes_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->io_collected_usec - p->last_io_collected_usec); +#ifdef __FreeBSD__ + pid_incremental_rate(io, p->io_storage_bytes_read, proc_info->ki_rusage.ru_inblock); + pid_incremental_rate(io, p->io_storage_bytes_written, proc_info->ki_rusage.ru_oublock); +#else + pid_incremental_rate(io, p->io_logical_bytes_read, str2kernel_uint_t(procfile_lineword(ff, 0, 1))); + pid_incremental_rate(io, p->io_logical_bytes_written, str2kernel_uint_t(procfile_lineword(ff, 1, 1))); + // pid_incremental_rate(io, p->io_read_calls, str2kernel_uint_t(procfile_lineword(ff, 2, 1))); + // pid_incremental_rate(io, p->io_write_calls, str2kernel_uint_t(procfile_lineword(ff, 3, 1))); + pid_incremental_rate(io, p->io_storage_bytes_read, str2kernel_uint_t(procfile_lineword(ff, 4, 1))); + pid_incremental_rate(io, p->io_storage_bytes_written, str2kernel_uint_t(procfile_lineword(ff, 5, 1))); + // pid_incremental_rate(io, p->io_cancelled_write_bytes, str2kernel_uint_t(procfile_lineword(ff, 6, 1))); +#endif if(unlikely(global_iterations_counter == 1)) { p->io_logical_bytes_read = 0; @@ -993,53 +1147,73 @@ cleanup: } static inline int read_proc_stat() { +#ifdef __FreeBSD__ + long cp_time[CPUSTATES]; + int i; + + if (unlikely(CPUSTATES != 5)) { + error("FREEBSD: There are %d CPU states (5 was expected)", CPUSTATES); + goto cleanup; + } + if (unlikely(GETSYSCTL("kern.cp_time", cp_time))) goto cleanup; +#else static char filename[FILENAME_MAX + 1] = ""; static procfile *ff = NULL; +#endif static kernel_uint_t utime_raw = 0, stime_raw = 0, gtime_raw = 0, gntime_raw = 0, ntime_raw = 0; static usec_t collected_usec = 0, last_collected_usec = 0; +#ifndef __FreeBSD__ if(unlikely(!ff)) { - snprintfz(filename, FILENAME_MAX, "%s/proc/stat", global_host_prefix); + snprintfz(filename, FILENAME_MAX, "%s/proc/stat", netdata_configured_host_prefix); ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) goto cleanup; } ff = procfile_readall(ff); if(unlikely(!ff)) goto cleanup; +#endif last_collected_usec = collected_usec; - collected_usec = now_realtime_usec(); - - file_counter++; - - kernel_uint_t last; + collected_usec = now_monotonic_usec(); - last = utime_raw; - utime_raw = str2ull(procfile_lineword(ff, 0, 1)); - global_utime = (utime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); + calls_counter++; - // nice time, on user time - last = ntime_raw; - ntime_raw = str2ull(procfile_lineword(ff, 0, 2)); - global_utime += (ntime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); + // temporary - it is added global_ntime; + kernel_uint_t global_ntime = 0; - last = stime_raw; - stime_raw = str2ull(procfile_lineword(ff, 0, 3)); - global_stime = (stime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); +#ifdef __FreeBSD__ + incremental_rate(global_utime, utime_raw, cp_time[0], collected_usec, last_collected_usec); + incremental_rate(global_ntime, ntime_raw, cp_time[1], collected_usec, last_collected_usec); + incremental_rate(global_stime, stime_raw, cp_time[2], collected_usec, last_collected_usec); +#else + incremental_rate(global_utime, utime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 1)), collected_usec, last_collected_usec); + incremental_rate(global_ntime, ntime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 2)), collected_usec, last_collected_usec); + incremental_rate(global_stime, stime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 3)), collected_usec, last_collected_usec); + incremental_rate(global_gtime, gtime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 10)), collected_usec, last_collected_usec); +#endif - last = gtime_raw; - gtime_raw = str2ull(procfile_lineword(ff, 0, 10)); - global_gtime = (gtime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); + global_utime += global_ntime; +#ifdef __FreeBSD__ + if(enable_guest_charts) { + enable_guest_charts = 0; + info("Guest charts aren't supported by FreeBSD"); + } +#else if(enable_guest_charts) { + // temporary - it is added global_ntime; + kernel_uint_t global_gntime = 0; + // guest nice time, on guest time - last = gntime_raw; - gntime_raw = str2ull(procfile_lineword(ff, 0, 11)); - global_gtime += (gntime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); + incremental_rate(global_gntime, gntime_raw, str2kernel_uint_t(procfile_lineword(ff, 0, 11)), collected_usec, last_collected_usec); + + global_gtime += global_gntime; // remove guest time from user time global_utime -= (global_utime > global_gtime) ? global_gtime : global_utime; } +#endif if(unlikely(global_iterations_counter == 1)) { global_utime = 0; @@ -1179,7 +1353,7 @@ static inline void all_files_grow() { all_files_size += FILE_DESCRIPTORS_INCREASE_STEP; } -static inline int file_descriptor_set_on_empty_slot(const char *name, uint32_t hash, int type) { +static inline int file_descriptor_set_on_empty_slot(const char *name, uint32_t hash, FD_FILETYPE type) { // check we have enough memory to add it if(!all_files || all_files_len == all_files_size) all_files_grow(); @@ -1260,21 +1434,26 @@ static inline int file_descriptor_find_or_add(const char *name) } // not found - int type; - if(name[0] == '/') type = FILETYPE_FILE; - else if(strncmp(name, "pipe:", 5) == 0) type = FILETYPE_PIPE; - else if(strncmp(name, "socket:", 7) == 0) type = FILETYPE_SOCKET; - else if(strcmp(name, "anon_inode:inotify") == 0 || strcmp(name, "inotify") == 0) type = FILETYPE_INOTIFY; - else if(strcmp(name, "anon_inode:[eventfd]") == 0) type = FILETYPE_EVENTFD; - else if(strcmp(name, "anon_inode:[eventpoll]") == 0) type = FILETYPE_EVENTPOLL; - else if(strcmp(name, "anon_inode:[timerfd]") == 0) type = FILETYPE_TIMERFD; - else if(strcmp(name, "anon_inode:[signalfd]") == 0) type = FILETYPE_SIGNALFD; - else if(strncmp(name, "anon_inode:", 11) == 0) { - if(unlikely(debug)) - fprintf(stderr, "apps.plugin: FIXME: unknown anonymous inode: %s\n", name); + FD_FILETYPE type; + if(likely(name[0] == '/')) type = FILETYPE_FILE; + else if(likely(strncmp(name, "pipe:", 5) == 0)) type = FILETYPE_PIPE; + else if(likely(strncmp(name, "socket:", 7) == 0)) type = FILETYPE_SOCKET; + else if(likely(strncmp(name, "anon_inode:", 11) == 0)) { + const char *t = &name[11]; + + if(strcmp(t, "inotify") == 0) type = FILETYPE_INOTIFY; + else if(strcmp(t, "[eventfd]") == 0) type = FILETYPE_EVENTFD; + else if(strcmp(t, "[eventpoll]") == 0) type = FILETYPE_EVENTPOLL; + else if(strcmp(t, "[timerfd]") == 0) type = FILETYPE_TIMERFD; + else if(strcmp(t, "[signalfd]") == 0) type = FILETYPE_SIGNALFD; + else { + if(unlikely(debug)) + fprintf(stderr, "apps.plugin: FIXME: unknown anonymous inode: %s\n", name); - type = FILETYPE_OTHER; + type = FILETYPE_OTHER; + } } + else if(likely(strcmp(name, "inotify") == 0)) type = FILETYPE_INOTIFY; else { if(unlikely(debug)) fprintf(stderr, "apps.plugin: FIXME: cannot understand linkname: %s\n", name); @@ -1294,8 +1473,9 @@ static inline void make_all_pid_fds_negative(struct pid_stat *p) { } static inline void cleanup_negative_pid_fds(struct pid_stat *p) { - int *fd = p->fds, *end = &p->fds[p->fds_size]; - while(fd < end) { + int *fd = p->fds, *fdend = &p->fds[p->fds_size]; + + while(fd < fdend) { if(unlikely(*fd < 0)) { file_descriptor_not_used(-(*fd)); *fd++ = 0; @@ -1310,10 +1490,132 @@ static inline void zero_pid_fds(struct pid_stat *p, int first, int size) { while(fd < end) *fd++ = 0; } -static inline int read_pid_file_descriptors(struct pid_stat *p) { +static inline int read_pid_file_descriptors(struct pid_stat *p, void *ptr) { + (void)ptr; +#ifdef __FreeBSD__ + int mib[4]; + size_t size; + struct kinfo_file *fds; + static char *fdsbuf; + char *bfdsbuf, *efdsbuf; + char fdsname[FILENAME_MAX + 1]; + + // we make all pid fds negative, so that + // we can detect unused file descriptors + // at the end, to free them + make_all_pid_fds_negative(p); + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_FILEDESC; + mib[3] = p->pid; + + if (unlikely(sysctl(mib, 4, NULL, &size, NULL, 0))) { + error("sysctl error: Can't get file descriptors data size for pid %d", p->pid); + return 0; + } + if (likely(size > 0)) + fdsbuf = reallocz(fdsbuf, size); + if (unlikely(sysctl(mib, 4, fdsbuf, &size, NULL, 0))) { + error("sysctl error: Can't get file descriptors data for pid %d", p->pid); + return 0; + } + + bfdsbuf = fdsbuf; + efdsbuf = fdsbuf + size; + while (bfdsbuf < efdsbuf) { + fds = (struct kinfo_file *)(uintptr_t)bfdsbuf; + if (unlikely(fds->kf_structsize == 0)) + break; + + // do not process file descriptors for current working directory, root directory, + // jail directory, ktrace vnode, text vnode and controlling terminal + if (unlikely(fds->kf_fd < 0)) { + bfdsbuf += fds->kf_structsize; + continue; + } + + // get file descriptors array index + int fdid = fds->kf_fd; + + // check if the fds array is small + if (unlikely(fdid >= p->fds_size)) { + // it is small, extend it + + if (unlikely(debug)) + fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + MAX_SPARE_FDS); + + p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(int)); + + // and initialize it + zero_pid_fds(p, p->fds_size, (fdid + MAX_SPARE_FDS) - p->fds_size); + p->fds_size = fdid + MAX_SPARE_FDS; + } + + if (unlikely(p->fds[fdid] == 0)) { + // we don't know this fd, get it + + switch (fds->kf_type) { + case KF_TYPE_FIFO: + case KF_TYPE_VNODE: + if (unlikely(!fds->kf_path[0])) { + sprintf(fdsname, "other: inode: %lu", fds->kf_un.kf_file.kf_file_fileid); + break; + } + sprintf(fdsname, "%s", fds->kf_path); + break; + case KF_TYPE_SOCKET: + switch (fds->kf_sock_domain) { + case AF_INET: + case AF_INET6: + if (fds->kf_sock_protocol == IPPROTO_TCP) + sprintf(fdsname, "socket: %d %lx", fds->kf_sock_protocol, fds->kf_un.kf_sock.kf_sock_inpcb); + else + sprintf(fdsname, "socket: %d %lx", fds->kf_sock_protocol, fds->kf_un.kf_sock.kf_sock_pcb); + break; + case AF_UNIX: + /* print address of pcb and connected pcb */ + sprintf(fdsname, "socket: %lx %lx", fds->kf_un.kf_sock.kf_sock_pcb, fds->kf_un.kf_sock.kf_sock_unpconn); + break; + default: + /* print protocol number and socket address */ + sprintf(fdsname, "socket: other: %d %s %s", fds->kf_sock_protocol, fds->kf_sa_local.__ss_pad1, fds->kf_sa_local.__ss_pad2); + } + break; + case KF_TYPE_PIPE: + sprintf(fdsname, "pipe: %lu %lu", fds->kf_un.kf_pipe.kf_pipe_addr, fds->kf_un.kf_pipe.kf_pipe_peer); + break; + case KF_TYPE_PTS: + sprintf(fdsname, "other: pts: %u", fds->kf_un.kf_pts.kf_pts_dev); + break; + case KF_TYPE_SHM: + sprintf(fdsname, "other: shm: %s size: %lu", fds->kf_path, fds->kf_un.kf_file.kf_file_size); + break; + case KF_TYPE_SEM: + sprintf(fdsname, "other: sem: %u", fds->kf_un.kf_sem.kf_sem_value); + break; + default: + sprintf(fdsname, "other: pid: %d fd: %d", fds->kf_un.kf_proc.kf_pid, fds->kf_fd); + } + + // if another process already has this, we will get + // the same id + p->fds[fdid] = file_descriptor_find_or_add(fdsname); + } + + // else make it positive again, we need it + // of course, the actual file may have changed, but we don't care so much + // FIXME: we could compare the inode as returned by readdir dirent structure + + else + p->fds[fdid] = -p->fds[fdid]; + + bfdsbuf += fds->kf_structsize; + } +#else if(unlikely(!p->fds_dirname)) { char dirname[FILENAME_MAX+1]; - snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", global_host_prefix, p->pid); + snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", netdata_configured_host_prefix, p->pid); p->fds_dirname = strdupz(dirname); } @@ -1336,7 +1638,7 @@ static inline int read_pid_file_descriptors(struct pid_stat *p) { continue; // get its number - int fdid = (int)str2l(de->d_name); + int fdid = (int) str2l(de->d_name); if(unlikely(fdid < 0)) continue; // check if the fds array is small @@ -1344,7 +1646,12 @@ static inline int read_pid_file_descriptors(struct pid_stat *p) { // it is small, extend it if(unlikely(debug)) - fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + MAX_SPARE_FDS); + fprintf(stderr + , "apps.plugin: extending fd memory slots for %s from %d to %d\n" + , p->comm + , p->fds_size + , fdid + MAX_SPARE_FDS + ); p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(int)); @@ -1356,7 +1663,7 @@ static inline int read_pid_file_descriptors(struct pid_stat *p) { if(unlikely(p->fds[fdid] == 0)) { // we don't know this fd, get it - sprintf(fdname, "%s/proc/%d/fd/%s", global_host_prefix, p->pid, de->d_name); + sprintf(fdname, "%s/proc/%d/fd/%s", netdata_configured_host_prefix, p->pid, de->d_name); ssize_t l = readlink(fdname, linkname, FILENAME_MAX); if(unlikely(l == -1)) { if(debug || (p->target && p->target->debug)) { @@ -1375,15 +1682,17 @@ static inline int read_pid_file_descriptors(struct pid_stat *p) { p->fds[fdid] = file_descriptor_find_or_add(linkname); } - // else make it positive again, we need it - // of course, the actual file may have changed, but we don't care so much - // FIXME: we could compare the inode as returned by readdir dirent structure + // else make it positive again, we need it + // of course, the actual file may have changed, but we don't care so much + // FIXME: we could compare the inode as returned by readdir dirent structure + // UPDATE: no we cannot use inodes - under /proc inodes don't change when the link is changed else p->fds[fdid] = -p->fds[fdid]; } closedir(fds); +#endif cleanup_negative_pid_fds(p); return 1; @@ -1530,11 +1839,11 @@ static inline void process_exited_processes() { if(p->updated || !p->stat_collected_usec) continue; - kernel_uint_t utime = (p->utime_raw + p->cutime_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); - kernel_uint_t stime = (p->stime_raw + p->cstime_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); - kernel_uint_t gtime = (p->gtime_raw + p->cgtime_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); - kernel_uint_t minflt = (p->minflt_raw + p->cminflt_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); - kernel_uint_t majflt = (p->majflt_raw + p->cmajflt_raw) * (1000000ULL * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + kernel_uint_t utime = (p->utime_raw + p->cutime_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + kernel_uint_t stime = (p->stime_raw + p->cstime_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + kernel_uint_t gtime = (p->gtime_raw + p->cgtime_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + kernel_uint_t minflt = (p->minflt_raw + p->cminflt_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); + kernel_uint_t majflt = (p->majflt_raw + p->cmajflt_raw) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec); if(utime + stime + gtime + minflt + majflt == 0) continue; @@ -1695,83 +2004,9 @@ static int compar_pid(const void *pid1, const void *pid2) { return 1; } -static inline int managed_log(struct pid_stat *p, uint32_t log, int status) { - if(unlikely(!status)) { - // error("command failed log %u, errno %d", log, errno); - - if(unlikely(debug || errno != ENOENT)) { - if(unlikely(debug || !(p->log_thrown & log))) { - p->log_thrown |= log; - switch(log) { - case PID_LOG_IO: - error("Cannot process %s/proc/%d/io (command '%s')", global_host_prefix, p->pid, p->comm); - break; - - case PID_LOG_STATM: - error("Cannot process %s/proc/%d/statm (command '%s')", global_host_prefix, p->pid, p->comm); - break; - - case PID_LOG_CMDLINE: - error("Cannot process %s/proc/%d/cmdline (command '%s')", global_host_prefix, p->pid, p->comm); - break; - - case PID_LOG_FDS: - error("Cannot process entries in %s/proc/%d/fd (command '%s')", global_host_prefix, p->pid, p->comm); - break; - - case PID_LOG_STAT: - break; - - default: - error("unhandled error for pid %d, command '%s'", p->pid, p->comm); - break; - } - } - } - errno = 0; - } - else if(unlikely(p->log_thrown & log)) { - // error("unsetting log %u on pid %d", log, p->pid); - p->log_thrown &= ~log; - } - - return status; -} - -static inline void assign_target_to_pid(struct pid_stat *p) { - uint32_t hash = simple_hash(p->comm); - size_t pclen = strlen(p->comm); - - struct target *w; - for(w = apps_groups_root_target; w ; w = w->next) { - // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm); - - // find it - 4 cases: - // 1. the target is not a pattern - // 2. the target has the prefix - // 3. the target has the suffix - // 4. the target is something inside cmdline - - if(unlikely(( (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm)) - || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen)) - || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen])) - || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && strstr(p->cmdline, w->compare)) - ))) { - - if(w->target) p->target = w->target; - else p->target = w; - - if(debug || (p->target && p->target->debug)) - fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name); - - break; - } - } -} - -static inline int collect_data_for_pid(pid_t pid) { - if(unlikely(pid <= 0 || pid > pid_max)) { - error("Invalid pid %d read (expected 1 to %d). Ignoring process.", pid, pid_max); +static inline int collect_data_for_pid(pid_t pid, void *ptr) { + if(unlikely(pid < INIT_PID || pid > pid_max)) { + error("Invalid pid %d read (expected %d to %d). Ignoring process.", pid, INIT_PID, pid_max); return 0; } @@ -1784,11 +2019,11 @@ static inline int collect_data_for_pid(pid_t pid) { // -------------------------------------------------------------------- // /proc//stat - if(unlikely(!managed_log(p, PID_LOG_STAT, read_proc_pid_stat(p)))) + if(unlikely(!managed_log(p, PID_LOG_STAT, read_proc_pid_stat(p, ptr)))) // there is no reason to proceed if we cannot get its status return 0; - read_proc_pid_ownership(p); + read_proc_pid_ownership(p, ptr); // check its parent pid if(unlikely(p->ppid < 0 || p->ppid > pid_max)) { @@ -1799,36 +2034,20 @@ static inline int collect_data_for_pid(pid_t pid) { // -------------------------------------------------------------------- // /proc//io - managed_log(p, PID_LOG_IO, read_proc_pid_io(p)); + managed_log(p, PID_LOG_IO, read_proc_pid_io(p, ptr)); // -------------------------------------------------------------------- // /proc//statm - if(unlikely(!managed_log(p, PID_LOG_STATM, read_proc_pid_statm(p)))) + if(unlikely(!managed_log(p, PID_LOG_STATM, read_proc_pid_statm(p, ptr)))) // there is no reason to proceed if we cannot get its memory status return 0; - // -------------------------------------------------------------------- - // link it - - // check if it is target - // we do this only once, the first time this pid is loaded - if(unlikely(p->new_entry)) { - // /proc//cmdline - if(likely(proc_pid_cmdline_is_needed)) - managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p)); - - if(unlikely(debug)) - fprintf(stderr, "apps.plugin: \tJust added %d (%s)\n", pid, p->comm); - - assign_target_to_pid(p); - } - // -------------------------------------------------------------------- // /proc//fd if(enable_file_charts) - managed_log(p, PID_LOG_FDS, read_pid_file_descriptors(p)); + managed_log(p, PID_LOG_FDS, read_pid_file_descriptors(p, ptr)); // -------------------------------------------------------------------- // done! @@ -1847,19 +2066,43 @@ static inline int collect_data_for_pid(pid_t pid) { static int collect_data_for_all_processes(void) { struct pid_stat *p = NULL; +#ifdef __FreeBSD__ + int i, procnum; + size_t procbase_size; + static struct kinfo_proc *procbase; + + int mib[3]; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PROC; + if (unlikely(sysctl(mib, 3, NULL, &procbase_size, NULL, 0))) { + error("sysctl error: Can't get processes data size"); + return 0; + } + procbase = reallocz(procbase, procbase_size); + if (unlikely(sysctl(mib, 3, procbase, &procbase_size, NULL, 0))) { + error("sysctl error: Can't get processes data"); + return 0; + } + procnum = procbase_size / sizeof(struct kinfo_proc); +#endif + if(all_pids_count) { size_t slc = 0; for(p = root_of_pids; p ; p = p->next) { p->read = 0; // mark it as not read, so that collect_data_for_pid() will read it p->updated = 0; - p->new_entry = 0; p->merged = 0; p->children_count = 0; p->parent = NULL; +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0) all_pids_sortlist[slc++] = p->pid; +#endif } +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0) if(unlikely(slc != all_pids_count)) { error("Internal error: I was thinking I had %zu processes in my arrays, but it seems there are more.", all_pids_count); all_pids_count = slc; @@ -1876,15 +2119,22 @@ static int collect_data_for_all_processes(void) { // we forward read all running processes // collect_data_for_pid() is smart enough, - // not to read the same pid twice per iterations + // not to read the same pid twice per iteration for(slc = 0; slc < all_pids_count; slc++) - collect_data_for_pid(all_pids_sortlist[slc]); + collect_data_for_pid(all_pids_sortlist[slc], NULL); } +#endif } +#ifdef __FreeBSD__ + for (i = INIT_PID; i < procnum - INIT_PID; ++i) { + pid_t pid = procbase[i].ki_pid; + collect_data_for_pid(pid, &procbase[i]); + } +#else char dirname[FILENAME_MAX + 1]; - snprintfz(dirname, FILENAME_MAX, "%s/proc", global_host_prefix); + snprintfz(dirname, FILENAME_MAX, "%s/proc", netdata_configured_host_prefix); DIR *dir = opendir(dirname); if(!dir) return 0; @@ -1902,9 +2152,10 @@ static int collect_data_for_all_processes(void) { if(unlikely(endptr == de->d_name || *endptr != '\0')) continue; - collect_data_for_pid(pid); + collect_data_for_pid(pid, NULL); } closedir(dir); +#endif if(!all_pids_count) return 0; @@ -1950,10 +2201,11 @@ static void cleanup_exited_pids(void) { if(unlikely(debug && (p->keep || p->keeploops))) fprintf(stderr, " > CLEANUP cannot keep exited process %d (%s) anymore - removing it.\n", p->pid, p->comm); - for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] > 0) { - file_descriptor_not_used(p->fds[c]); - p->fds[c] = 0; - } + for(c = 0; c < p->fds_size; c++) + if(p->fds[c] > 0) { + file_descriptor_not_used(p->fds[c]); + p->fds[c] = 0; + } pid_t r = p->pid; p = p->next; @@ -2016,7 +2268,7 @@ static void apply_apps_groups_targets_inheritance(void) { && p->parent && p->parent->children_count && (p->target == p->parent->target || !p->parent->target) - && p->ppid != 1 + && p->ppid != INIT_PID )) { p->parent->children_count--; p->merged = 1; @@ -2038,8 +2290,8 @@ static void apply_apps_groups_targets_inheritance(void) { } // init goes always to default target - if(all_pids[1]) - all_pids[1]->target = apps_groups_default_target; + if(all_pids[INIT_PID]) + all_pids[INIT_PID]->target = apps_groups_default_target; // give a default target on all top level processes if(unlikely(debug)) loops++; @@ -2191,7 +2443,7 @@ static inline void aggregate_fd_on_target(int fd, struct target *w) { w->openeventpolls++; break; - default: + case FILETYPE_OTHER: w->openother++; break; } @@ -2365,7 +2617,7 @@ static usec_t send_resource_usage_to_netdata() { usec_t cpusyst; if(!last.tv_sec) { - now_realtime_timeval(&last); + now_monotonic_timeval(&last); getrusage(RUSAGE_SELF, &me_last); // the first time, give a zero to allow @@ -2376,7 +2628,7 @@ static usec_t send_resource_usage_to_netdata() { cpusyst = 0; } else { - now_realtime_timeval(&now); + now_monotonic_timeval(&now); getrusage(RUSAGE_SELF, &me); usec = dt_usec(&now, &last); @@ -2387,12 +2639,50 @@ static usec_t send_resource_usage_to_netdata() { memmove(&me_last, &me, sizeof(struct rusage)); } + static char created_charts = 0; + if(unlikely(!created_charts)) { + created_charts = 1; + + fprintf(stdout + , "CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' apps.plugin netdata.apps_cpu stacked 140000 %1$d\n" + "DIMENSION user '' incremental 1 1000\n" + "DIMENSION system '' incremental 1 1000\n" + "CHART netdata.apps_files '' 'Apps Plugin Files' 'files/s' apps.plugin netdata.apps_files line 140001 %1$d\n" + "DIMENSION calls '' incremental 1 1\n" + "DIMENSION files '' incremental 1 1\n" + "DIMENSION pids '' absolute 1 1\n" + "DIMENSION fds '' absolute 1 1\n" + "DIMENSION targets '' absolute 1 1\n" + "CHART netdata.apps_fix '' 'Apps Plugin Normalization Ratios' 'percentage' apps.plugin netdata.apps_fix line 140002 %1$d\n" + "DIMENSION utime '' absolute 1 %2$llu\n" + "DIMENSION stime '' absolute 1 %2$llu\n" + "DIMENSION gtime '' absolute 1 %2$llu\n" + "DIMENSION minflt '' absolute 1 %2$llu\n" + "DIMENSION majflt '' absolute 1 %2$llu\n" + , update_every + , RATES_DETAIL + ); + + if(include_exited_childs) + fprintf(stdout + , "CHART netdata.apps_children_fix '' 'Apps Plugin Exited Children Normalization Ratios' 'percentage' apps.plugin netdata.apps_children_fix line 140003 %1$d\n" + "DIMENSION cutime '' absolute 1 %2$llu\n" + "DIMENSION cstime '' absolute 1 %2$llu\n" + "DIMENSION cgtime '' absolute 1 %2$llu\n" + "DIMENSION cminflt '' absolute 1 %2$llu\n" + "DIMENSION cmajflt '' absolute 1 %2$llu\n" + , update_every + , RATES_DETAIL + ); + } + fprintf(stdout, "BEGIN netdata.apps_cpu %llu\n" "SET user = %llu\n" "SET system = %llu\n" "END\n" "BEGIN netdata.apps_files %llu\n" + "SET calls = %zu\n" "SET files = %zu\n" "SET pids = %zu\n" "SET fds = %d\n" @@ -2409,6 +2699,7 @@ static usec_t send_resource_usage_to_netdata() { , cpuuser , cpusyst , usec + , calls_counter , file_counter , all_pids_count , all_files_len @@ -2662,6 +2953,7 @@ static void send_collected_data_to_netdata(struct target *root, const char *type } send_END(); +#ifndef __FreeBSD__ send_BEGIN(type, "lreads", usec); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) @@ -2675,6 +2967,7 @@ static void send_collected_data_to_netdata(struct target *root, const char *type send_SET(w->name, w->io_logical_bytes_written); } send_END(); +#endif send_BEGIN(type, "preads", usec); for (w = root; w ; w = w->next) { @@ -2800,6 +3093,7 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL); } +#ifndef __FreeBSD__ fprintf(stdout, "CHART %s.lreads '' '%s Disk Logical Reads' 'kilobytes/s' disk %s.lreads stacked 20042 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) @@ -2811,7 +3105,21 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); } +#endif + +#ifdef __FreeBSD__ + fprintf(stdout, "CHART %s.preads '' '%s Disk Reads' 'blocks/s' disk %s.preads stacked 20002 %d\n", type, title, type, update_every); + for (w = root; w ; w = w->next) { + if(unlikely(w->exposed)) + fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL); + } + fprintf(stdout, "CHART %s.pwrites '' '%s Disk Writes' 'blocks/s' disk %s.pwrites stacked 20002 %d\n", type, title, type, update_every); + for (w = root; w ; w = w->next) { + if(unlikely(w->exposed)) + fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, RATES_DETAIL); + } +#else fprintf(stdout, "CHART %s.preads '' '%s Disk Reads' 'kilobytes/s' disk %s.preads stacked 20002 %d\n", type, title, type, update_every); for (w = root; w ; w = w->next) { if(unlikely(w->exposed)) @@ -2823,6 +3131,7 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type if(unlikely(w->exposed)) fprintf(stdout, "DIMENSION %s '' absolute 1 %llu\n", w->name, 1024LLU * RATES_DETAIL); } +#endif if(enable_file_charts) { fprintf(stdout, "CHART %s.files '' '%s Open Files' 'open files' disk %s.files stacked 20050 %d\n", type, @@ -2852,6 +3161,22 @@ static void send_charts_updates_to_netdata(struct target *root, const char *type // ---------------------------------------------------------------------------- // parse command line arguments +int check_proc_1_io() { + int ret = 0; + + procfile *ff = procfile_open("/proc/1/io", NULL, PROCFILE_FLAG_NO_ERROR_ON_FILE_IO); + if(!ff) goto cleanup; + + ff = procfile_readall(ff); + if(!ff) goto cleanup; + + ret = 1; + +cleanup: + procfile_close(ff); + return ret; +} + static void parse_args(int argc, char **argv) { int i, freq = 0; @@ -2871,6 +3196,15 @@ static void parse_args(int argc, char **argv) exit(0); } + if(strcmp("test-permissions", argv[i]) == 0 || strcmp("-t", argv[i]) == 0) { + if(!check_proc_1_io()) { + perror("Tried to read /proc/1/io and it failed"); + exit(1); + } + printf("OK\n"); + exit(0); + } + if(strcmp("debug", argv[i]) == 0) { debug = 1; // debug_flags = 0xffffffff; @@ -2922,7 +3256,7 @@ static void parse_args(int argc, char **argv) "\n" " netdata apps.plugin %s\n" " Copyright (C) 2016-2017 Costa Tsaousis \n" - " Released under GNU Public License v3 or later.\n" + " Released under GNU General Public License v3 or later.\n" " All rights reserved.\n" "\n" " This program is a data collector plugin for netdata.\n" @@ -2975,15 +3309,74 @@ static void parse_args(int argc, char **argv) } } -int main(int argc, char **argv) -{ +static int am_i_running_as_root() { + uid_t uid = getuid(), euid = geteuid(); + + if(uid == 0 || euid == 0) { + if(debug) info("I am running with escalated privileges, uid = %u, euid = %u.", uid, euid); + return 1; + } + + if(debug) info("I am not running with escalated privileges, uid = %u, euid = %u.", uid, euid); + return 0; +} + +#ifdef HAVE_CAPABILITY +static int check_capabilities() { + cap_t caps = cap_get_proc(); + if(!caps) { + error("Cannot get current capabilities."); + return 0; + } + else if(debug) + info("Received my capabilities from the system."); + + int ret = 1; + + cap_flag_value_t cfv = CAP_CLEAR; + if(cap_get_flag(caps, CAP_DAC_READ_SEARCH, CAP_EFFECTIVE, &cfv) == -1) { + error("Cannot find if CAP_DAC_READ_SEARCH is effective."); + ret = 0; + } + else { + if(cfv != CAP_SET) { + error("apps.plugin should run with CAP_DAC_READ_SEARCH."); + ret = 0; + } + else if(debug) + info("apps.plugin runs with CAP_DAC_READ_SEARCH."); + } + + cfv = CAP_CLEAR; + if(cap_get_flag(caps, CAP_SYS_PTRACE, CAP_EFFECTIVE, &cfv) == -1) { + error("Cannot find if CAP_SYS_PTRACE is effective."); + ret = 0; + } + else { + if(cfv != CAP_SET) { + error("apps.plugin should run with CAP_SYS_PTRACE."); + ret = 0; + } + else if(debug) + info("apps.plugin runs with CAP_SYS_PTRACE."); + } + + cap_free(caps); + + return ret; +} +#else +static int check_capabilities() { + return 0; +} +#endif + +int main(int argc, char **argv) { // debug_flags = D_PROCFILE; // set the name for logging program_name = "apps.plugin"; - info("started on pid %d", getpid()); - // disable syslog for apps.plugin error_log_syslog = 0; @@ -2991,12 +3384,12 @@ int main(int argc, char **argv) error_log_errors_per_period = 100; error_log_throttle_period = 3600; - global_host_prefix = getenv("NETDATA_HOST_PREFIX"); - if(global_host_prefix == NULL) { + netdata_configured_host_prefix = getenv("NETDATA_HOST_PREFIX"); + if(netdata_configured_host_prefix == NULL) { // info("NETDATA_HOST_PREFIX is not passed from netdata"); - global_host_prefix = ""; + netdata_configured_host_prefix = ""; } - // else info("Found NETDATA_HOST_PREFIX='%s'", global_host_prefix); + // else info("Found NETDATA_HOST_PREFIX='%s'", netdata_configured_host_prefix); config_dir = getenv("NETDATA_CONFIG_DIR"); if(config_dir == NULL) { @@ -3010,58 +3403,53 @@ int main(int argc, char **argv) struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY }; if(setrlimit(RLIMIT_CORE, &rl) != 0) info("Cannot request unlimited core dumps for debugging... Proceeding anyway..."); +#ifdef HAVE_SYS_PRCTL_H prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); +#endif } #endif /* NETDATA_INTERNAL_CHECKS */ procfile_adaptive_initial_allocation = 1; - time_t started_t = now_realtime_sec(); + time_t started_t = now_monotonic_sec(); get_system_HZ(); get_system_pid_max(); get_system_cpus(); parse_args(argc, argv); - all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max); - all_pids = callocz(sizeof(struct pid_stat *), (size_t) pid_max); - - fprintf(stdout, - "CHART netdata.apps_cpu '' 'Apps Plugin CPU' 'milliseconds/s' apps.plugin netdata.apps_cpu stacked 140000 %1$d\n" - "DIMENSION user '' incremental 1 1000\n" - "DIMENSION system '' incremental 1 1000\n" - "CHART netdata.apps_files '' 'Apps Plugin Files' 'files/s' apps.plugin netdata.apps_files line 140001 %1$d\n" - "DIMENSION files '' incremental 1 1\n" - "DIMENSION pids '' absolute 1 1\n" - "DIMENSION fds '' absolute 1 1\n" - "DIMENSION targets '' absolute 1 1\n" - "CHART netdata.apps_fix '' 'Apps Plugin Normalization Ratios' 'percentage' apps.plugin netdata.apps_fix line 140002 %1$d\n" - "DIMENSION utime '' absolute 1 %2$llu\n" - "DIMENSION stime '' absolute 1 %2$llu\n" - "DIMENSION gtime '' absolute 1 %2$llu\n" - "DIMENSION minflt '' absolute 1 %2$llu\n" - "DIMENSION majflt '' absolute 1 %2$llu\n" - , update_every - , RATES_DETAIL + if(!check_capabilities() && !am_i_running_as_root() && !check_proc_1_io()) { + uid_t uid = getuid(), euid = geteuid(); +#ifdef HAVE_CAPABILITY + error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. " + "Without these, apps.plugin cannot report disk I/O utilization of other processes. " + "To enable capabilities run: sudo setcap cap_dac_read_search,cap_sys_ptrace+ep %s; " + "To enable setuid to root run: sudo chown root %s; sudo chmod 4755 %s; " + , uid, euid, argv[0], argv[0], argv[0] + ); +#else + error("apps.plugin should either run as root (now running with uid %u, euid %u) or have special capabilities. " + "Without these, apps.plugin cannot report disk I/O utilization of other processes. " + "Your system does not support capabilities. " + "To enable setuid to root run: sudo chown root %s; sudo chmod 4755 %s; " + , uid, euid, argv[0], argv[0] ); +#endif + } - if(include_exited_childs) - fprintf(stdout, - "CHART netdata.apps_children_fix '' 'Apps Plugin Exited Children Normalization Ratios' 'percentage' apps.plugin netdata.apps_children_fix line 140003 %1$d\n" - "DIMENSION cutime '' absolute 1 %2$llu\n" - "DIMENSION cstime '' absolute 1 %2$llu\n" - "DIMENSION cgtime '' absolute 1 %2$llu\n" - "DIMENSION cminflt '' absolute 1 %2$llu\n" - "DIMENSION cmajflt '' absolute 1 %2$llu\n" - , update_every - , RATES_DETAIL - ); + info("started on pid %d", getpid()); + +#if (ALL_PIDS_ARE_READ_INSTANTLY == 0) + all_pids_sortlist = callocz(sizeof(pid_t), (size_t)pid_max); +#endif + + all_pids = callocz(sizeof(struct pid_stat *), (size_t) pid_max); usec_t step = update_every * USEC_PER_SEC; global_iterations_counter = 1; + heartbeat_t hb; + heartbeat_init(&hb); for(;1; global_iterations_counter++) { - usec_t now = now_realtime_usec(); - usec_t next = now - (now % step) + step; #ifdef NETDATA_PROFILING #warning "compiling for profiling" @@ -3069,10 +3457,7 @@ int main(int argc, char **argv) profiling_count++; if(unlikely(profiling_count > 1000)) exit(0); #else - while(now < next) { - sleep_usec(next - now); - now = now_realtime_usec(); - } + heartbeat_next(&hb, step); #endif if(!collect_data_for_all_processes()) { @@ -3110,9 +3495,7 @@ int main(int argc, char **argv) if(unlikely(debug)) fprintf(stderr, "apps.plugin: done Loop No %zu\n", global_iterations_counter); - time_t current_t = now_realtime_sec(); - // restart check (14400 seconds) - if(current_t - started_t > 14400) exit(0); + if(now_monotonic_sec() - started_t > 14400) exit(0); } }