@cryptotaxi247 / netdata-1 / commits / 66fd5099a

apps.plugin optimization to eliminate several unneeded calls

Costa Tsaousis (ktsaou) committed Jan 24, 2017 at 01:10 UTC 66fd5099afc2cf324cb720af2725d0fb7b6acf12
3 files changed +170 -106
src/apps_plugin.c
+132 -89
@@ -475,6 +475,8 @@ struct pid_stat {
475 unsigned long long io_collected_usec;
476 unsigned long long last_io_collected_usec;
477
478 + char *fds_dirname; // the full directory name in /proc/PID/fd
479 +
480 char *stat_filename;
481 char *statm_filename;
482 char *io_filename;
@@ -522,11 +524,12 @@ static inline void del_pid_entry(pid_t pid) {
524 if(all_pids[pid]->next) all_pids[pid]->next->prev = all_pids[pid]->prev;
525 if(all_pids[pid]->prev) all_pids[pid]->prev->next = all_pids[pid]->next;
526
525 - if(all_pids[pid]->fds) freez(all_pids[pid]->fds);
526 - if(all_pids[pid]->stat_filename) freez(all_pids[pid]->stat_filename);
527 - if(all_pids[pid]->statm_filename) freez(all_pids[pid]->statm_filename);
528 - if(all_pids[pid]->io_filename) freez(all_pids[pid]->io_filename);
529 - if(all_pids[pid]->cmdline_filename) freez(all_pids[pid]->cmdline_filename);
527 + freez(all_pids[pid]->fds);
528 + freez(all_pids[pid]->fds_dirname);
529 + freez(all_pids[pid]->stat_filename);
530 + freez(all_pids[pid]->statm_filename);
531 + freez(all_pids[pid]->io_filename);
532 + freez(all_pids[pid]->cmdline_filename);
533 freez(all_pids[pid]);
534
535 all_pids[pid] = NULL;
@@ -604,7 +607,8 @@ static inline int read_proc_pid_stat(struct pid_stat *p) {
607 if(unlikely(!ff)) goto cleanup;
608
609 // if(set_quotes) procfile_set_quotes(ff, "()");
607 - if(set_quotes) procfile_set_open_close(ff, "(", ")");
610 + if(unlikely(set_quotes))
611 + procfile_set_open_close(ff, "(", ")");
612
613 ff = procfile_readall(ff);
614 if(unlikely(!ff)) goto cleanup;
@@ -615,7 +619,8 @@ static inline int read_proc_pid_stat(struct pid_stat *p) {
619
620 // p->pid = str2ul(procfile_lineword(ff, 0, 0+i));
621
618 - strncpyz(p->comm, procfile_lineword(ff, 0, 1), MAX_COMPARE_NAME);
622 + if(unlikely(!p->comm[0]))
623 + strncpyz(p->comm, procfile_lineword(ff, 0, 1), MAX_COMPARE_NAME);
624
625 // p->state = *(procfile_lineword(ff, 0, 2));
626 p->ppid = (int32_t)str2ul(procfile_lineword(ff, 0, 3));
@@ -1172,74 +1177,106 @@ static inline int file_descriptor_find_or_add(const char *name)
1177 return file_descriptor_set_on_empty_slot(name, hash, type);
1178 }
1179
1180 +static inline void make_all_pid_fds_negative(struct pid_stat *p) {
1181 + int *fd = p->fds, *end = &p->fds[p->fds_size];
1182 + while(fd < end) {
1183 + *fd = -(*fd);
1184 + fd++;
1185 + }
1186 +}
1187 +
1188 +static inline void cleanup_negative_pid_fds(struct pid_stat *p) {
1189 + int *fd = p->fds, *end = &p->fds[p->fds_size];
1190 + while(fd < end) {
1191 + if(unlikely(*fd < 0)) {
1192 + file_descriptor_not_used(-(*fd));
1193 + *fd++ = 0;
1194 + }
1195 + else
1196 + fd++;
1197 + }
1198 +}
1199 +
1200 +static inline void zero_pid_fds(struct pid_stat *p, int first, int size) {
1201 + int *fd = &p->fds[first], *end = &p->fds[first + size];
1202 + while(fd < end) *fd++ = 0;
1203 +}
1204 +
1205 static inline int read_pid_file_descriptors(struct pid_stat *p) {
1176 - char dirname[FILENAME_MAX+1];
1177 -
1178 - snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", global_host_prefix, p->pid);
1179 - DIR *fds = opendir(dirname);
1180 - if(fds) {
1181 - int c;
1182 - struct dirent *de;
1183 - char fdname[FILENAME_MAX + 1];
1184 - char linkname[FILENAME_MAX + 1];
1185 -
1186 - // make the array negative
1187 - for(c = 0 ; c < p->fds_size ; c++)
1188 - p->fds[c] = -p->fds[c];
1189 -
1190 - while((de = readdir(fds))) {
1191 - if(strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
1192 - continue;
1206 + if(unlikely(!p->fds_dirname)) {
1207 + char dirname[FILENAME_MAX+1];
1208 + snprintfz(dirname, FILENAME_MAX, "%s/proc/%d/fd", global_host_prefix, p->pid);
1209 + p->fds_dirname = strdupz(dirname);
1210 + }
1211
1194 - // check if the fds array is small
1195 - int fdid = (int)str2l(de->d_name);
1196 - if(fdid < 0) continue;
1197 - if(fdid >= p->fds_size) {
1198 - // it is small, extend it
1199 - if(unlikely(debug))
1200 - fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + MAX_SPARE_FDS);
1212 + DIR *fds = opendir(p->fds_dirname);
1213 + if(unlikely(!fds)) return 0;
1214
1202 - p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(int));
1215 + struct dirent *de;
1216 + char fdname[FILENAME_MAX + 1];
1217 + char linkname[FILENAME_MAX + 1];
1218
1204 - // and initialize it
1205 - for(c = p->fds_size ; c < (fdid + MAX_SPARE_FDS) ; c++) p->fds[c] = 0;
1206 - p->fds_size = fdid + MAX_SPARE_FDS;
1207 - }
1219 + // we make all pid fds negative, so that
1220 + // we can detect unused file descriptors
1221 + // at the end, to free them
1222 + make_all_pid_fds_negative(p);
1223 +
1224 + while((de = readdir(fds))) {
1225 + // we need only files with numeric names
1226 +
1227 + if(unlikely(de->d_name[0] < '0' || de->d_name[0] > '9'))
1228 + continue;
1229
1209 - if(p->fds[fdid] == 0) {
1210 - // we don't know this fd, get it
1211 -
1212 - sprintf(fdname, "%s/proc/%d/fd/%s", global_host_prefix, p->pid, de->d_name);
1213 - ssize_t l = readlink(fdname, linkname, FILENAME_MAX);
1214 - if(l == -1) {
1215 - if(debug || (p->target && p->target->debug)) {
1216 - if(debug || (p->target && p->target->debug))
1217 - error("Cannot read link %s", fdname);
1218 - }
1219 - continue;
1230 + // get its number
1231 + int fdid = (int)str2l(de->d_name);
1232 + if(unlikely(fdid < 0)) continue;
1233 +
1234 + // check if the fds array is small
1235 + if(unlikely(fdid >= p->fds_size)) {
1236 + // it is small, extend it
1237 +
1238 + if(unlikely(debug))
1239 + fprintf(stderr, "apps.plugin: extending fd memory slots for %s from %d to %d\n", p->comm, p->fds_size, fdid + MAX_SPARE_FDS);
1240 +
1241 + p->fds = reallocz(p->fds, (fdid + MAX_SPARE_FDS) * sizeof(int));
1242 +
1243 + // and initialize it
1244 + zero_pid_fds(p, p->fds_size, (fdid + MAX_SPARE_FDS) - p->fds_size);
1245 + p->fds_size = fdid + MAX_SPARE_FDS;
1246 + }
1247 +
1248 + if(unlikely(p->fds[fdid] == 0)) {
1249 + // we don't know this fd, get it
1250 +
1251 + sprintf(fdname, "%s/proc/%d/fd/%s", global_host_prefix, p->pid, de->d_name);
1252 + ssize_t l = readlink(fdname, linkname, FILENAME_MAX);
1253 + if(unlikely(l == -1)) {
1254 + if(debug || (p->target && p->target->debug)) {
1255 + if(debug || (p->target && p->target->debug))
1256 + error("Cannot read link %s", fdname);
1257 }
1258 + continue;
1259 + }
1260 + else
1261 linkname[l] = '\0';
1222 - file_counter++;
1262
1224 - // if another process already has this, we will get
1225 - // the same id
1226 - p->fds[fdid] = file_descriptor_find_or_add(linkname);
1227 - }
1263 + file_counter++;
1264
1229 - // else make it positive again, we need it
1230 - // of course, the actual file may have changed, but we don't care so much
1231 - // FIXME: we could compare the inode as returned by readdir dirent structure
1232 - else p->fds[fdid] = -p->fds[fdid];
1265 + // if another process already has this, we will get
1266 + // the same id
1267 + p->fds[fdid] = file_descriptor_find_or_add(linkname);
1268 }
1234 - closedir(fds);
1269
1236 - // remove all the negative file descriptors
1237 - for(c = 0 ; c < p->fds_size ; c++) if(p->fds[c] < 0) {
1238 - file_descriptor_not_used(-p->fds[c]);
1239 - p->fds[c] = 0;
1240 - }
1270 + // else make it positive again, we need it
1271 + // of course, the actual file may have changed, but we don't care so much
1272 + // FIXME: we could compare the inode as returned by readdir dirent structure
1273 +
1274 + else
1275 + p->fds[fdid] = -p->fds[fdid];
1276 }
1242 - else return 0;
1277 +
1278 + closedir(fds);
1279 + cleanup_negative_pid_fds(p);
1280
1281 return 1;
1282 }
@@ -1594,6 +1631,37 @@ static inline int managed_log(struct pid_stat *p, uint32_t log, int status) {
1631 return status;
1632 }
1633
1634 +static inline void assign_target_to_pid(struct pid_stat *p) {
1635 + uint32_t hash = simple_hash(p->comm);
1636 + size_t pclen = strlen(p->comm);
1637 +
1638 + struct target *w;
1639 + for(w = apps_groups_root_target; w ; w = w->next) {
1640 + // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm);
1641 +
1642 + // find it - 4 cases:
1643 + // 1. the target is not a pattern
1644 + // 2. the target has the prefix
1645 + // 3. the target has the suffix
1646 + // 4. the target is something inside cmdline
1647 +
1648 + if(unlikely(( (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm))
1649 + || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen))
1650 + || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen]))
1651 + || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && strstr(p->cmdline, w->compare))
1652 + ))) {
1653 +
1654 + if(w->target) p->target = w->target;
1655 + else p->target = w;
1656 +
1657 + if(debug || (p->target && p->target->debug))
1658 + fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
1659 +
1660 + break;
1661 + }
1662 + }
1663 +}
1664 +
1665 static inline int collect_data_for_pid(pid_t pid) {
1666 if(unlikely(pid <= 0 || pid > pid_max)) {
1667 error("Invalid pid %d read (expected 1 to %d). Ignoring process.", pid, pid_max);
@@ -1646,32 +1714,7 @@ static inline int collect_data_for_pid(pid_t pid) {
1714 if(unlikely(debug))
1715 fprintf(stderr, "apps.plugin: \tJust added %d (%s)\n", pid, p->comm);
1716
1649 - uint32_t hash = simple_hash(p->comm);
1650 - size_t pclen = strlen(p->comm);
1651 -
1652 - struct target *w;
1653 - for(w = apps_groups_root_target; w ; w = w->next) {
1654 - // if(debug || (p->target && p->target->debug)) fprintf(stderr, "apps.plugin: \t\tcomparing '%s' with '%s'\n", w->compare, p->comm);
1655 -
1656 - // find it - 4 cases:
1657 - // 1. the target is not a pattern
1658 - // 2. the target has the prefix
1659 - // 3. the target has the suffix
1660 - // 4. the target is something inside cmdline
1661 - if( (!w->starts_with && !w->ends_with && w->comparehash == hash && !strcmp(w->compare, p->comm))
1662 - || (w->starts_with && !w->ends_with && !strncmp(w->compare, p->comm, w->comparelen))
1663 - || (!w->starts_with && w->ends_with && pclen >= w->comparelen && !strcmp(w->compare, &p->comm[pclen - w->comparelen]))
1664 - || (proc_pid_cmdline_is_needed && w->starts_with && w->ends_with && strstr(p->cmdline, w->compare))
1665 - ) {
1666 - if(w->target) p->target = w->target;
1667 - else p->target = w;
1668 -
1669 - if(debug || (p->target && p->target->debug))
1670 - fprintf(stderr, "apps.plugin: \t\t%s linked to target %s\n", p->comm, p->target->name);
1671 -
1672 - break;
1673 - }
1674 - }
1717 + assign_target_to_pid(p);
1718 }
1719
1720 // --------------------------------------------------------------------
src/procfile.c
+34 -16
@@ -15,6 +15,25 @@ size_t procfile_max_lines = PFLINES_INCREASE_STEP;
15 size_t procfile_max_words = PFWORDS_INCREASE_STEP;
16 size_t procfile_max_allocation = PROCFILE_INCREMENT_BUFFER;
17
18 +
19 +// ----------------------------------------------------------------------------
20 +
21 +char *procfile_filename(procfile *ff) {
22 + char buffer[FILENAME_MAX + 1];
23 + snprintfz(buffer, FILENAME_MAX, "/proc/self/fd/%d", ff->fd);
24 +
25 + ssize_t l = readlink(buffer, ff->filename, FILENAME_MAX);
26 + if(unlikely(l == -1))
27 + snprintfz(ff->filename, FILENAME_MAX, "unknown filename for fd %d", ff->fd);
28 + else
29 + ff->filename[l] = '\0';
30 +
31 + // on non-linux systems, something like this will be needed
32 + // fcntl(ff->fd, F_GETPATH, ff->filename)
33 +
34 + return ff->filename;
35 +}
36 +
37 // ----------------------------------------------------------------------------
38 // An array of words
39
@@ -114,7 +133,7 @@ static inline void pflines_free(pflines *fl) {
133 #define PF_CHAR_IS_CLOSE 'C'
134
135 void procfile_close(procfile *ff) {
117 - debug(D_PROCFILE, PF_PREFIX ": Closing file '%s'", ff->filename);
136 + debug(D_PROCFILE, PF_PREFIX ": Closing file '%s'", procfile_filename(ff));
137
138 if(likely(ff->lines)) pflines_free(ff->lines);
139 if(likely(ff->words)) pfwords_free(ff->words);
@@ -250,26 +269,24 @@ static inline void procfile_parser(procfile *ff) {
269 }
270
271 procfile *procfile_readall(procfile *ff) {
253 - debug(D_PROCFILE, PF_PREFIX ": Reading file '%s'.", ff->filename);
254 -
255 - ssize_t r = 1;
256 - ff->len = 0;
272 + // debug(D_PROCFILE, PF_PREFIX ": Reading file '%s'.", ff->filename);
273
258 - while(likely(r > 0)) {
274 + ff->len = 0; // zero the used size
275 + ssize_t r = 1; // read at least once
276 + while(r > 0) {
277 ssize_t s = ff->len;
278 ssize_t x = ff->size - s;
279
280 if(unlikely(!x)) {
263 - debug(D_PROCFILE, PF_PREFIX ": Expanding data buffer for file '%s'.", ff->filename);
264 -
281 + debug(D_PROCFILE, PF_PREFIX ": Expanding data buffer for file '%s'.", procfile_filename(ff));
282 ff = reallocz(ff, sizeof(procfile) + ff->size + PROCFILE_INCREMENT_BUFFER);
283 ff->size += PROCFILE_INCREMENT_BUFFER;
284 }
285
269 - debug(D_PROCFILE, "Reading file '%s', from position %ld with length %lu", ff->filename, s, ff->size - s);
286 + debug(D_PROCFILE, "Reading file '%s', from position %ld with length %lu", procfile_filename(ff), s, ff->size - s);
287 r = read(ff->fd, &ff->data[s], ff->size - s);
288 if(unlikely(r == -1)) {
272 - if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot read from file '%s'", ff->filename);
289 + if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot read from file '%s'", procfile_filename(ff));
290 procfile_close(ff);
291 return NULL;
292 }
@@ -277,9 +294,9 @@ procfile *procfile_readall(procfile *ff) {
294 ff->len += r;
295 }
296
280 - debug(D_PROCFILE, "Rewinding file '%s'", ff->filename);
297 + // debug(D_PROCFILE, "Rewinding file '%s'", ff->filename);
298 if(unlikely(lseek(ff->fd, 0, SEEK_SET) == -1)) {
282 - if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot rewind on file '%s'.", ff->filename);
299 + if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO))) error(PF_PREFIX ": Cannot rewind on file '%s'.", procfile_filename(ff));
300 procfile_close(ff);
301 return NULL;
302 }
@@ -294,7 +311,7 @@ procfile *procfile_readall(procfile *ff) {
311 if(unlikely(ff->words->len > procfile_max_words)) procfile_max_words = ff->words->len;
312 }
313
297 - debug(D_PROCFILE, "File '%s' updated.", ff->filename);
314 + // debug(D_PROCFILE, "File '%s' updated.", ff->filename);
315 return ff;
316 }
317
@@ -379,7 +396,8 @@ procfile *procfile_open(const char *filename, const char *separators, uint32_t f
396
397 size_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_allocation : PROCFILE_INCREMENT_BUFFER;
398 procfile *ff = mallocz(sizeof(procfile) + size);
382 - strncpyz(ff->filename, filename, FILENAME_MAX);
399 +
400 + //strncpyz(ff->filename, filename, FILENAME_MAX);
401
402 ff->fd = fd;
403 ff->size = size;
@@ -406,7 +424,7 @@ procfile *procfile_reopen(procfile *ff, const char *filename, const char *separa
424 return NULL;
425 }
426
409 - strncpyz(ff->filename, filename, FILENAME_MAX);
427 + //strncpyz(ff->filename, filename, FILENAME_MAX);
428
429 ff->flags = flags;
430
@@ -423,7 +441,7 @@ void procfile_print(procfile *ff) {
441 size_t lines = procfile_lines(ff), l;
442 char *s;
443
426 - debug(D_PROCFILE, "File '%s' with %zu lines and %zu words", ff->filename, ff->lines->len, ff->words->len);
444 + debug(D_PROCFILE, "File '%s' with %zu lines and %zu words", procfile_filename(ff), ff->lines->len, ff->words->len);
445
446 for(l = 0; likely(l < lines) ;l++) {
447 size_t words = procfile_linewords(ff, l);
src/procfile.h
+4 -1
@@ -59,7 +59,8 @@ typedef struct {
59 #define PROCFILE_FLAG_NO_ERROR_ON_FILE_IO 0x00000001
60
61 typedef struct {
62 - char filename[FILENAME_MAX + 1];
62 + char filename[FILENAME_MAX + 1]; // not populated until profile_filename() is called
63 +
64 uint32_t flags;
65 int fd; // the file desriptor
66 size_t len; // the bytes we have placed into data
@@ -89,6 +90,8 @@ extern void procfile_print(procfile *ff);
90 extern void procfile_set_quotes(procfile *ff, const char *quotes);
91 extern void procfile_set_open_close(procfile *ff, const char *open, const char *close);
92
93 +extern char *procfile_filename(procfile *ff);
94 +
95 // ----------------------------------------------------------------------------
96
97 // set this to 1, to have procfile adapt its initial buffer allocation to the max allocation used so far