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
#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;
const char *name;
uint32_t hash;
- char type;
+ FD_FILETYPE type;
int count;
int pos;
} *all_files = NULL;
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()
// 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')", 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;
+ }
+ }
+}
+
// ----------------------------------------------------------------------------
// update pids from proc
p->stat_collected_usec = now_monotonic_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->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));
+ // 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));
- kernel_uint_t last;
+ 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);
+ }
+
+ strncpyz(p->comm, comm, MAX_COMPARE_NAME);
- last = p->minflt_raw;
- p->minflt_raw = str2kernel_unit_t(procfile_lineword(ff, 0, 9));
+ // /proc/<pid>/cmdline
+ if(likely(proc_pid_cmdline_is_needed))
+ managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(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_unit_t(procfile_lineword(ff, 0, 10));
+ 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_unit_t(procfile_lineword(ff, 0, 11));
+ 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_unit_t(procfile_lineword(ff, 0, 12));
+ 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_unit_t(procfile_lineword(ff, 0, 13));
+ 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);
last = p->stime_raw;
- p->stime_raw = str2kernel_unit_t(procfile_lineword(ff, 0, 14));
+ 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_unit_t(procfile_lineword(ff, 0, 15));
+ 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_unit_t(procfile_lineword(ff, 0, 16));
+ 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->priority = str2kernel_unit_t(procfile_lineword(ff, 0, 17));
- // p->nice = str2kernel_unit_t(procfile_lineword(ff, 0, 18));
+ // 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_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_unit_t(procfile_lineword(ff, 0, 43));
+ 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);
if (show_guest_time || p->gtime || p->cgtime) {
file_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));
+ 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));
return 1;
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_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 = str2ull(procfile_lineword(ff, 1, 1));
+ 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 = str2ull(procfile_lineword(ff, 2, 1));
+ // 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 = str2ull(procfile_lineword(ff, 3, 1));
+ // 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 = str2ull(procfile_lineword(ff, 4, 1));
+ 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 = str2ull(procfile_lineword(ff, 5, 1));
+ 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 = str2ull(procfile_lineword(ff, 6, 1));
+ // 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);
if(unlikely(global_iterations_counter == 1)) {
kernel_uint_t last;
last = utime_raw;
- utime_raw = str2ull(procfile_lineword(ff, 0, 1));
+ 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);
// nice time, on user time
last = ntime_raw;
- ntime_raw = str2ull(procfile_lineword(ff, 0, 2));
+ 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 = str2ull(procfile_lineword(ff, 0, 3));
+ 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);
last = gtime_raw;
- gtime_raw = str2ull(procfile_lineword(ff, 0, 10));
+ 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);
if(enable_guest_charts) {
// guest nice time, on guest time
last = gntime_raw;
- gntime_raw = str2ull(procfile_lineword(ff, 0, 11));
+ 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);
// remove guest time from user time
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();
}
// 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);
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);
// 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/<pid>/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/<pid>/fd
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;
w->openeventpolls++;
break;
- default:
+ case FILETYPE_OTHER:
w->openother++;
break;
}
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 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"
}
}
-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
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 */
parse_args(argc, argv);
+ if(!check_capabilities()) {
+ if(!am_i_running_as_root()) {
+ 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
+ }
+ }
+
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(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
- );
-
usec_t step = update_every * USEC_PER_SEC;
global_iterations_counter = 1;
heartbeat_t hb;