@cryptotaxi247 / netdata-1 / commits / 6ee478054

apps.plugin detects in-place process changes; fixes #1698

Costa Tsaousis (ktsaou) committed Feb 1, 2017 at 23:02 UTC 6ee4780546b133905f488296747f915a5fbb9de1
4 files changed +150 -149
src/apps_plugin.c
+137 -136
@@ -293,7 +293,6 @@ struct pid_stat {
293 int keeploops; // increases by 1 every time keep is 1 and updated 0
294 char updated:1; // 1 when the process is currently running
295 char merged:1; // 1 when it has been merged to its parent
296 - char new_entry:1; // 1 when this is a new process, just saw for the first time
296 char read:1; // 1 when we have already read this process for this iteration
297
298 int sortlist; // higher numbers = top on the process tree
@@ -627,56 +626,132 @@ static int read_apps_groups_conf(const char *file)
626 // struct pid_stat management
627
628 static inline struct pid_stat *get_pid_entry(pid_t pid) {
630 - if(unlikely(all_pids[pid])) {
631 - all_pids[pid]->new_entry = 0;
629 + if(unlikely(all_pids[pid]))
630 return all_pids[pid];
633 - }
631
635 - all_pids[pid] = callocz(sizeof(struct pid_stat), 1);
636 - all_pids[pid]->fds = callocz(sizeof(int), MAX_SPARE_FDS);
637 - all_pids[pid]->fds_size = MAX_SPARE_FDS;
632 + struct pid_stat *p = callocz(sizeof(struct pid_stat), 1);
633 + p->fds = callocz(sizeof(int), MAX_SPARE_FDS);
634 + p->fds_size = MAX_SPARE_FDS;
635
636 if(likely(root_of_pids))
640 - root_of_pids->prev = all_pids[pid];
637 + root_of_pids->prev = p;
638
642 - all_pids[pid]->next = root_of_pids;
643 - root_of_pids = all_pids[pid];
639 + p->next = root_of_pids;
640 + root_of_pids = p;
641
645 - all_pids[pid]->pid = pid;
646 - all_pids[pid]->new_entry = 1;
642 + p->pid = pid;
643
644 + all_pids[pid] = p;
645 all_pids_count++;
646
650 - return all_pids[pid];
647 + return p;
648 }
649
650 static inline void del_pid_entry(pid_t pid) {
654 - if(unlikely(!all_pids[pid])) {
651 + struct pid_stat *p = all_pids[pid];
652 +
653 + if(unlikely(!p)) {
654 error("attempted to free pid %d that is not allocated.", pid);
655 return;
656 }
657
658 if(unlikely(debug))
660 - fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, all_pids[pid]->comm);
659 + fprintf(stderr, "apps.plugin: process %d %s exited, deleting it.\n", pid, p->comm);
660
662 - if(root_of_pids == all_pids[pid])
663 - root_of_pids = all_pids[pid]->next;
661 + if(root_of_pids == p)
662 + root_of_pids = p->next;
663
665 - if(all_pids[pid]->next) all_pids[pid]->next->prev = all_pids[pid]->prev;
666 - if(all_pids[pid]->prev) all_pids[pid]->prev->next = all_pids[pid]->next;
664 + if(p->next) p->next->prev = p->prev;
665 + if(p->prev) p->prev->next = p->next;
666
668 - freez(all_pids[pid]->fds);
669 - freez(all_pids[pid]->fds_dirname);
670 - freez(all_pids[pid]->stat_filename);
671 - freez(all_pids[pid]->statm_filename);
672 - freez(all_pids[pid]->io_filename);
673 - freez(all_pids[pid]->cmdline_filename);
674 - freez(all_pids[pid]);
667 + freez(p->fds);
668 + freez(p->fds_dirname);
669 + freez(p->stat_filename);
670 + freez(p->statm_filename);
671 + freez(p->io_filename);
672 + freez(p->cmdline_filename);
673 + freez(p);
674
675 all_pids[pid] = NULL;
676 all_pids_count--;
677 }
678
679 +// ----------------------------------------------------------------------------
680 +
681 +static inline int managed_log(struct pid_stat *p, uint32_t log, int status) {
682 + if(unlikely(!status)) {
683 + // error("command failed log %u, errno %d", log, errno);
684 +
685 + if(unlikely(debug || errno != ENOENT)) {
686 + if(unlikely(debug || !(p->log_thrown & log))) {
687 + p->log_thrown |= log;
688 + switch(log) {
689 + case PID_LOG_IO:
690 + error("Cannot process %s/proc/%d/io (command '%s')", global_host_prefix, p->pid, p->comm);
691 + break;
692 +
693 + case PID_LOG_STATM:
694 + error("Cannot process %s/proc/%d/statm (command '%s')", global_host_prefix, p->pid, p->comm);
695 + break;
696 +
697 + case PID_LOG_CMDLINE:
698 + error("Cannot process %s/proc/%d/cmdline (command '%s')", global_host_prefix, p->pid, p->comm);
699 + break;
700 +
701 + case PID_LOG_FDS:
702 + error("Cannot process entries in %s/proc/%d/fd (command '%s')", global_host_prefix, p->pid, p->comm);
703 + break;
704 +
705 + case PID_LOG_STAT:
706 + break;
707 +
708 + default:
709 + error("unhandled error for pid %d, command '%s'", p->pid, p->comm);
710 + break;
711 + }
712 + }
713 + }
714 + errno = 0;
715 + }
716 + else if(unlikely(p->log_thrown & log)) {
717 + // error("unsetting log %u on pid %d", log, p->pid);
718 + p->log_thrown &= ~log;
719 + }
720 +
721 + return status;
722 +}
723 +
724 +static inline void assign_target_to_pid(struct pid_stat *p) {
725 + uint32_t hash = simple_hash(p->comm);
726 + size_t pclen = strlen(p->comm);
727 +
728 + struct target *w;
729 + for(w = apps_groups_root_target; w ; w = w->next) {
730 + // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm);
731 +
732 + // find it - 4 cases:
733 + // 1. the target is not a pattern
734 + // 2. the target has the prefix
735 + // 3. the target has the suffix
736 + // 4. the target is something inside cmdline
737 +
738 + if(unlikely(( (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm))
739 + || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen))
740 + || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen]))
741 + || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && strstr(p->cmdline, w->compare))
742 + ))) {
743 +
744 + if(w->target) p->target = w->target;
745 + else p->target = w;
746 +
747 + if(debug || (p->target && p->target->debug))
748 + fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
749 +
750 + break;
751 + }
752 + }
753 +}
754 +
755
756 // ----------------------------------------------------------------------------
757 // update pids from proc
@@ -758,11 +833,8 @@ static inline int read_proc_pid_stat(struct pid_stat *p) {
833 p->stat_collected_usec = now_monotonic_usec();
834 file_counter++;
835
761 - // p->pid = str2pid_t(procfile_lineword(ff, 0, 0+i));
762 -
763 - if(unlikely(!p->comm[0]))
764 - strncpyz(p->comm, procfile_lineword(ff, 0, 1), MAX_COMPARE_NAME);
765 -
836 + // p->pid = str2pid_t(procfile_lineword(ff, 0, 0));
837 + char *comm = procfile_lineword(ff, 0, 1);
838 // p->state = *(procfile_lineword(ff, 0, 2));
839 p->ppid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 3));
840 // p->pgrp = (int32_t)str2pid_t(procfile_lineword(ff, 0, 4));
@@ -771,9 +843,24 @@ static inline int read_proc_pid_stat(struct pid_stat *p) {
843 // p->tpgid = (int32_t)str2pid_t(procfile_lineword(ff, 0, 7));
844 // p->flags = str2uint64_t(procfile_lineword(ff, 0, 8));
845
774 - kernel_uint_t last;
846 + if(strcmp(p->comm, comm)) {
847 + if(unlikely(debug)) {
848 + if(p->comm[0])
849 + fprintf(stderr, "apps.plugin: \tpid %d (%s) changed name to '%s'\n", p->pid, p->comm, comm);
850 + else
851 + fprintf(stderr, "apps.plugin: \tJust added %d (%s)\n", p->pid, p->comm);
852 + }
853 +
854 + strncpyz(p->comm, comm, MAX_COMPARE_NAME);
855 +
856 + // /proc/<pid>/cmdline
857 + if(likely(proc_pid_cmdline_is_needed))
858 + managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p));
859 +
860 + assign_target_to_pid(p);
861 + }
862
776 - last = p->minflt_raw;
863 + kernel_uint_t last = p->minflt_raw;
864 p->minflt_raw = str2kernel_uint_t(procfile_lineword(ff, 0, 9));
865 p->minflt = (p->minflt_raw - last) * (USEC_PER_SEC * RATES_DETAIL) / (p->stat_collected_usec - p->last_stat_collected_usec);
866
@@ -1262,20 +1349,25 @@ static inline int file_descriptor_find_or_add(const char *name)
1349 // not found
1350
1351 FD_FILETYPE type;
1265 - if(name[0] == '/') type = FILETYPE_FILE;
1266 - else if(strncmp(name, "pipe:", 5) == 0) type = FILETYPE_PIPE;
1267 - else if(strncmp(name, "socket:", 7) == 0) type = FILETYPE_SOCKET;
1268 - else if(strcmp(name, "anon_inode:inotify") == 0 || strcmp(name, "inotify") == 0) type = FILETYPE_INOTIFY;
1269 - else if(strcmp(name, "anon_inode:[eventfd]") == 0) type = FILETYPE_EVENTFD;
1270 - else if(strcmp(name, "anon_inode:[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
1271 - else if(strcmp(name, "anon_inode:[timerfd]") == 0) type = FILETYPE_TIMERFD;
1272 - else if(strcmp(name, "anon_inode:[signalfd]") == 0) type = FILETYPE_SIGNALFD;
1273 - else if(strncmp(name, "anon_inode:", 11) == 0) {
1274 - if(unlikely(debug))
1275 - fprintf(stderr, "apps.plugin: FIXME: unknown anonymous inode: %s\n", name);
1352 + if(likely(name[0] == '/')) type = FILETYPE_FILE;
1353 + else if(likely(strncmp(name, "pipe:", 5) == 0)) type = FILETYPE_PIPE;
1354 + else if(likely(strncmp(name, "socket:", 7) == 0)) type = FILETYPE_SOCKET;
1355 + else if(likely(strncmp(name, "anon_inode:", 11) == 0)) {
1356 + const char *t = &name[11];
1357 +
1358 + if(strcmp(t, "inotify") == 0) type = FILETYPE_INOTIFY;
1359 + else if(strcmp(t, "[eventfd]") == 0) type = FILETYPE_EVENTFD;
1360 + else if(strcmp(t, "[eventpoll]") == 0) type = FILETYPE_EVENTPOLL;
1361 + else if(strcmp(t, "[timerfd]") == 0) type = FILETYPE_TIMERFD;
1362 + else if(strcmp(t, "[signalfd]") == 0) type = FILETYPE_SIGNALFD;
1363 + else {
1364 + if(unlikely(debug))
1365 + fprintf(stderr, "apps.plugin: FIXME: unknown anonymous inode: %s\n", name);
1366
1277 - type = FILETYPE_OTHER;
1367 + type = FILETYPE_OTHER;
1368 + }
1369 }
1370 + else if(likely(strcmp(name, "inotify") == 0)) type = FILETYPE_INOTIFY;
1371 else {
1372 if(unlikely(debug))
1373 fprintf(stderr, "apps.plugin: FIXME: cannot understand linkname: %s\n", name);
@@ -1696,80 +1788,6 @@ static int compar_pid(const void *pid1, const void *pid2) {
1788 return 1;
1789 }
1790
1699 -static inline int managed_log(struct pid_stat *p, uint32_t log, int status) {
1700 - if(unlikely(!status)) {
1701 - // error("command failed log %u, errno %d", log, errno);
1702 -
1703 - if(unlikely(debug || errno != ENOENT)) {
1704 - if(unlikely(debug || !(p->log_thrown & log))) {
1705 - p->log_thrown |= log;
1706 - switch(log) {
1707 - case PID_LOG_IO:
1708 - error("Cannot process %s/proc/%d/io (command '%s')", global_host_prefix, p->pid, p->comm);
1709 - break;
1710 -
1711 - case PID_LOG_STATM:
1712 - error("Cannot process %s/proc/%d/statm (command '%s')", global_host_prefix, p->pid, p->comm);
1713 - break;
1714 -
1715 - case PID_LOG_CMDLINE:
1716 - error("Cannot process %s/proc/%d/cmdline (command '%s')", global_host_prefix, p->pid, p->comm);
1717 - break;
1718 -
1719 - case PID_LOG_FDS:
1720 - error("Cannot process entries in %s/proc/%d/fd (command '%s')", global_host_prefix, p->pid, p->comm);
1721 - break;
1722 -
1723 - case PID_LOG_STAT:
1724 - break;
1725 -
1726 - default:
1727 - error("unhandled error for pid %d, command '%s'", p->pid, p->comm);
1728 - break;
1729 - }
1730 - }
1731 - }
1732 - errno = 0;
1733 - }
1734 - else if(unlikely(p->log_thrown & log)) {
1735 - // error("unsetting log %u on pid %d", log, p->pid);
1736 - p->log_thrown &= ~log;
1737 - }
1738 -
1739 - return status;
1740 -}
1741 -
1742 -static inline void assign_target_to_pid(struct pid_stat *p) {
1743 - uint32_t hash = simple_hash(p->comm);
1744 - size_t pclen = strlen(p->comm);
1745 -
1746 - struct target *w;
1747 - for(w = apps_groups_root_target; w ; w = w->next) {
1748 - // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm);
1749 -
1750 - // find it - 4 cases:
1751 - // 1. the target is not a pattern
1752 - // 2. the target has the prefix
1753 - // 3. the target has the suffix
1754 - // 4. the target is something inside cmdline
1755 -
1756 - if(unlikely(( (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm))
1757 - || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen))
1758 - || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen]))
1759 - || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && strstr(p->cmdline, w->compare))
1760 - ))) {
1761 -
1762 - if(w->target) p->target = w->target;
1763 - else p->target = w;
1764 -
1765 - if(debug || (p->target && p->target->debug))
1766 - fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
1767 -
1768 - break;
1769 - }
1770 - }
1771 -}
1772 -
1791 static inline int collect_data_for_pid(pid_t pid) {
1792 if(unlikely(pid <= 0 || pid > pid_max)) {
1793 error("Invalid pid %d read (expected 1 to %d). Ignoring process.", pid, pid_max);
@@ -1809,22 +1827,6 @@ static inline int collect_data_for_pid(pid_t pid) {
1827 // there is no reason to proceed if we cannot get its memory status
1828 return 0;
1829
1812 - // --------------------------------------------------------------------
1813 - // link it
1814 -
1815 - // check if it is target
1816 - // we do this only once, the first time this pid is loaded
1817 - if(unlikely(p->new_entry)) {
1818 - // /proc/<pid>/cmdline
1819 - if(likely(proc_pid_cmdline_is_needed))
1820 - managed_log(p, PID_LOG_CMDLINE, read_proc_pid_cmdline(p));
1821 -
1822 - if(unlikely(debug))
1823 - fprintf(stderr, "apps.plugin: \tJust added %d (%s)\n", pid, p->comm);
1824 -
1825 - assign_target_to_pid(p);
1826 - }
1827 -
1830 // --------------------------------------------------------------------
1831 // /proc/<pid>/fd
1832
@@ -1853,7 +1855,6 @@ static int collect_data_for_all_processes(void) {
1855 for(p = root_of_pids; p ; p = p->next) {
1856 p->read = 0; // mark it as not read, so that collect_data_for_pid() will read it
1857 p->updated = 0;
1856 - p->new_entry = 0;
1858 p->merged = 0;
1859 p->children_count = 0;
1860 p->parent = NULL;
src/common.c
-11
@@ -1060,17 +1060,6 @@ char *fgets_trim_len(char *buf, size_t buf_size, FILE *fp, size_t *len) {
1060 return s;
1061 }
1062
1063 -char *strncpyz(char *dst, const char *src, size_t n) {
1064 - char *p = dst;
1065 -
1066 - while (*src && n--)
1067 - *dst++ = *src++;
1068 -
1069 - *dst = '\0';
1070 -
1071 - return p;
1072 -}
1073 -
1063 int vsnprintfz(char *dst, size_t n, const char *fmt, va_list args) {
1064 int size = vsnprintf(dst, n, fmt, args);
1065
src/common.h
-1
@@ -221,7 +221,6 @@ extern void strreverse(char* begin, char* end);
221 extern char *mystrsep(char **ptr, char *s);
222 extern char *trim(char *s);
223
224 -extern char *strncpyz(char *dst, const char *src, size_t n);
224 extern int vsnprintfz(char *dst, size_t n, const char *fmt, va_list args);
225 extern int snprintfz(char *dst, size_t n, const char *fmt, ...) PRINTFLIKE(3, 4);
226
src/inlined.h
+13 -1
@@ -128,12 +128,24 @@ static inline unsigned long long str2ull(const char *s) {
128 #undef strcmp
129 #endif
130 #define strcmp(a, b) strsame(a, b)
131 +#endif // NETDATA_STRCMP_OVERRIDE
132 +
133 static inline int strsame(const char *a, const char *b) {
134 if(unlikely(a == b)) return 0;
135 while(*a && *a == *b) { a++; b++; }
136 return *a - *b;
137 }
136 -#endif // NETDATA_STRSAME
138 +
139 +static inline char *strncpyz(char *dst, const char *src, size_t n) {
140 + char *p = dst;
141 +
142 + while (*src && n--)
143 + *dst++ = *src++;
144 +
145 + *dst = '\0';
146 +
147 + return p;
148 +}
149
150 static inline int read_single_number_file(const char *filename, unsigned long long *result) {
151 char buffer[30 + 1];