X-Git-Url: https://arthur.barton.de/gitweb/?a=blobdiff_plain;f=src%2Fapps_plugin.c;h=82b77df655ff5a945c1b5e327f80adfd9fb24b97;hb=ce381953ca239d092b3085ee29798a4908f32747;hp=f5c25c7366ecb27364654a8fbd55e39b1d12435d;hpb=ead21b07e31cd1df630292e19d2dc0f434aa9840;p=netdata.git diff --git a/src/apps_plugin.c b/src/apps_plugin.c index f5c25c73..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 @@ -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 @@ -759,6 +781,17 @@ static inline void assign_target_to_pid(struct pid_stat *p) { 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", netdata_configured_host_prefix, p->pid); @@ -772,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++) @@ -788,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; @@ -807,9 +850,36 @@ 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)) { @@ -829,11 +899,16 @@ 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_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)); @@ -843,6 +918,7 @@ static inline int read_proc_pid_stat(struct pid_stat *p) { // 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 if(strcmp(p->comm, comm)) { if(unlikely(debug)) { @@ -861,38 +937,31 @@ static inline int read_proc_pid_stat(struct pid_stat *p) { assign_target_to_pid(p); } - kernel_uint_t last = p->minflt_raw; - p->minflt_raw = str2kernel_uint_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_uint_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); - - last = p->majflt_raw; - p->majflt_raw = str2kernel_uint_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); - - last = p->cmajflt_raw; - p->cmajflt_raw = str2kernel_uint_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); - - last = p->utime_raw; - p->utime_raw = str2kernel_uint_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); +#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->stime_raw; - p->stime_raw = str2kernel_uint_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); - - last = p->cutime_raw; - p->cutime_raw = str2kernel_uint_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); - - last = p->cstime_raw; - p->cstime_raw = str2kernel_uint_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; + 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)); @@ -920,13 +989,9 @@ static inline int read_proc_pid_stat(struct pid_stat *p) { // p->delayacct_blkio_ticks = str2kernel_uint_t(procfile_lineword(ff, 0, 41)); if(enable_guest_charts) { - last = p->gtime_raw; - p->gtime_raw = str2kernel_uint_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_uint_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; @@ -934,6 +999,7 @@ 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", 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); @@ -969,7 +1035,11 @@ 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)) { @@ -983,9 +1053,15 @@ static inline int read_proc_pid_statm(struct pid_stat *p) { ff = procfile_readall(ff); if(unlikely(!ff)) goto cleanup; +#endif calls_counter++; +#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)); @@ -993,6 +1069,7 @@ static inline int read_proc_pid_statm(struct pid_stat *p) { // 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; @@ -1007,7 +1084,11 @@ 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)) { @@ -1022,41 +1103,25 @@ static inline int read_proc_pid_io(struct pid_stat *p) { ff = procfile_readall(ff); if(unlikely(!ff)) goto cleanup; +#endif calls_counter++; p->last_io_collected_usec = p->io_collected_usec; p->io_collected_usec = now_monotonic_usec(); - kernel_uint_t last; - - last = p->io_logical_bytes_read_raw; - p->io_logical_bytes_read_raw = str2kernel_uint_t(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 = str2kernel_uint_t(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 = str2kernel_uint_t(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 = str2kernel_uint_t(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); - - last = p->io_storage_bytes_read_raw; - p->io_storage_bytes_read_raw = str2kernel_uint_t(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 = str2kernel_uint_t(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 = str2kernel_uint_t(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; @@ -1082,11 +1147,23 @@ 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", netdata_configured_host_prefix); ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT); @@ -1095,40 +1172,48 @@ static inline int read_proc_stat() { ff = procfile_readall(ff); if(unlikely(!ff)) goto cleanup; +#endif last_collected_usec = collected_usec; collected_usec = now_monotonic_usec(); calls_counter++; - kernel_uint_t last; - - last = utime_raw; - utime_raw = str2kernel_uint_t(procfile_lineword(ff, 0, 1)); - global_utime = (utime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); + // temporary - it is added global_ntime; + kernel_uint_t global_ntime = 0; - // nice time, on user time - last = ntime_raw; - ntime_raw = str2kernel_uint_t(procfile_lineword(ff, 0, 2)); - global_utime += (ntime_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (collected_usec - last_collected_usec); - - last = stime_raw; - stime_raw = str2kernel_uint_t(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 = str2kernel_uint_t(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 = str2kernel_uint_t(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; @@ -1405,7 +1490,129 @@ 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", netdata_configured_host_prefix, p->pid); @@ -1485,6 +1692,7 @@ static inline int read_pid_file_descriptors(struct pid_stat *p) { } closedir(fds); +#endif cleanup_negative_pid_fds(p); return 1; @@ -1631,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; @@ -1796,9 +2004,9 @@ static int compar_pid(const void *pid1, const void *pid2) { return 1; } -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; } @@ -1811,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)) { @@ -1826,12 +2034,12 @@ 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; @@ -1839,7 +2047,7 @@ static inline int collect_data_for_pid(pid_t pid) { // /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! @@ -1858,6 +2066,28 @@ 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) { @@ -1867,9 +2097,12 @@ static int collect_data_for_all_processes(void) { 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; @@ -1886,12 +2119,19 @@ 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", netdata_configured_host_prefix); @@ -1912,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; @@ -2027,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; @@ -2049,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++; @@ -2712,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)) @@ -2725,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) { @@ -2850,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)) @@ -2861,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)) @@ -2873,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, @@ -3180,7 +3439,10 @@ int main(int argc, char **argv) { 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;