0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034 #include <stdlib.h>
0035 #include <stdio.h>
0036 #include <ndrstandard.h>
0037 #include <nstdutil.h>
0038 #include <nstd_tls.h>
0039 #include <string.h>
0040 #include "thlock.h"
0041 #include "userlog.h"
0042 #include "ndebug.h"
0043 #include "utlist.h"
0044 #include "exregex.h"
0045 #include "sys_unix.h"
0046 #include <exmemck.h>
0047 #include <errno.h>
0048
0049
0050 #define MIN_STATS 2
0051
0052
0053
0054
0055 exmemck_config_t *M_config = NULL;
0056 exmemck_process_t *M_proc = NULL;
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067 exprivate exmemck_config_t * get_config(char *mask, int autocreate, int *p_ret,
0068 int *p_is_new)
0069 {
0070 int stat = EXSUCCEED;
0071 exmemck_config_t * ret = NULL;
0072
0073 EXHASH_FIND_STR(M_config, mask, ret);
0074
0075 if (NULL==ret && autocreate)
0076 {
0077
0078 if (NULL==(ret=NDRX_CALLOC(1, sizeof(exmemck_config_t))))
0079 {
0080 int err = errno;
0081 NDRX_LOG(log_error, "Failed to allocate xmemck_config_t: %s",
0082 strerror(err));
0083
0084 userlog("Failed to allocate xmemck_config_t: %s",
0085 strerror(err));
0086 EXFAIL_OUT(stat);
0087 }
0088
0089 NDRX_STRCPY_SAFE(ret->mask, mask);
0090
0091 EXHASH_ADD_STR(M_config, mask, ret);
0092
0093
0094 if (EXSUCCEED!=ndrx_regcomp(&ret->mask_regex, mask))
0095 {
0096 NDRX_LOG(log_error, "Failed to compile mask [%s]", mask);
0097 ret = NULL;
0098 stat = EXFAIL;
0099 goto out;
0100 }
0101
0102 *p_is_new = EXTRUE;
0103
0104 NDRX_LOG(log_debug, "allocate new config: [%s] %d",
0105 ret->mask, ret->settings.mem_limit);
0106 }
0107
0108 out:
0109
0110 if (EXSUCCEED!=stat && NULL!=ret)
0111 {
0112 EXHASH_DEL(M_config, ret);
0113 NDRX_FREE(ret);
0114 ret=NULL;
0115 }
0116
0117 return ret;
0118 }
0119
0120
0121
0122
0123 exprivate void dump_config(exmemck_config_t * cfg)
0124 {
0125 NDRX_LOG(log_debug, "--- Config entry, mask: [%s] ------", cfg->mask);
0126 NDRX_LOG(log_debug, "inherited defaults from mask: [%s]", cfg->dlft_mask);
0127 NDRX_LOG(log_debug, "mem_limit : [%ld]", cfg->settings.mem_limit);
0128 NDRX_LOG(log_debug, "percent_diff_allow : [%ld]", cfg->settings.percent_diff_allow);
0129 NDRX_LOG(log_debug, "interval_start_prcnt : [%ld]", cfg->settings.interval_start_prcnt);
0130 NDRX_LOG(log_debug, "interval_stop_prcnt : [%ld]", cfg->settings.interval_stop_prcnt);
0131 NDRX_LOG(log_debug, "flags : [0x%lx]", cfg->settings.flags);
0132 NDRX_LOG(log_debug, "interval_mon : [%d]", cfg->settings.interval_mon);
0133 NDRX_LOG(log_debug, "pf_proc_exit : [%p]", cfg->settings.pf_proc_exit);
0134 NDRX_LOG(log_debug, "pf_proc_leaky : [%p]", cfg->settings.pf_proc_leaky);
0135 NDRX_LOG(log_debug, "------------------------------------");
0136 }
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146 expublic int ndrx_memck_add(char *mask, char *dlft_mask, exmemck_settings_t *p_settings )
0147 {
0148 int ret = EXSUCCEED;
0149 int is_new = EXFALSE;
0150
0151 exmemck_config_t * cfg, *dflt;
0152
0153 NDRX_LOG(log_debug, "%s: enter, mask: [%s]", __func__, mask);
0154
0155 cfg = get_config(mask, EXTRUE, &ret, &is_new);
0156
0157 if (NULL==cfg || EXSUCCEED!=ret)
0158 {
0159 NDRX_LOG(log_error, "%s: failed to get config for mask [%s]",
0160 __func__, mask);
0161 EXFAIL_OUT(ret);
0162 }
0163
0164
0165 if (is_new && NULL!=dlft_mask)
0166 {
0167 NDRX_LOG(log_debug, "Making init for defaults: [%s]", dlft_mask);
0168
0169
0170 dflt = get_config(dlft_mask, EXFALSE, &ret, NULL);
0171
0172 if (NULL!=dflt)
0173 {
0174
0175
0176 NDRX_LOG(log_debug, "Got defaults...");
0177 memcpy(&(cfg->settings), &(dflt->settings), sizeof(cfg->settings));
0178
0179 NDRX_STRCPY_SAFE(cfg->dlft_mask, dlft_mask);
0180 }
0181 }
0182
0183 if (EXEOS!=p_settings->negative_mask[0])
0184 {
0185 NDRX_STRCPY_SAFE(cfg->settings.negative_mask, p_settings->negative_mask);
0186
0187 if (EXSUCCEED!=ndrx_regcomp(&cfg->neg_mask_regex, cfg->settings.negative_mask))
0188 {
0189 NDRX_LOG(log_error, "Failed to compile negative mask [%s]",
0190 cfg->settings.negative_mask);
0191 EXFAIL_OUT(ret);
0192 }
0193 cfg->neg_mask_used = EXTRUE;
0194 }
0195
0196 if (EXFAIL < p_settings->flags)
0197 {
0198 cfg->settings.flags = p_settings->flags;
0199 }
0200
0201 if (EXFAIL < p_settings->interval_start_prcnt)
0202 {
0203 cfg->settings.interval_start_prcnt = p_settings->interval_start_prcnt;
0204 }
0205
0206 if (EXFAIL < p_settings->interval_stop_prcnt)
0207 {
0208 cfg->settings.interval_stop_prcnt = p_settings->interval_stop_prcnt;
0209 }
0210
0211 if (EXFAIL < p_settings->mem_limit)
0212 {
0213 cfg->settings.mem_limit = p_settings->mem_limit;
0214 }
0215
0216 if (EXFAIL < p_settings->percent_diff_allow)
0217 {
0218 cfg->settings.percent_diff_allow = p_settings->percent_diff_allow;
0219 }
0220
0221 if (EXFAIL < p_settings->min_values)
0222 {
0223 cfg->settings.min_values = p_settings->min_values;
0224 }
0225
0226 if (NULL!=p_settings->pf_proc_exit)
0227 {
0228 cfg->settings.pf_proc_exit = p_settings->pf_proc_exit;
0229 }
0230
0231 if (NULL!=p_settings->pf_proc_leaky)
0232 {
0233 cfg->settings.pf_proc_leaky = p_settings->pf_proc_leaky;
0234 }
0235
0236
0237 dump_config(cfg);
0238
0239 out:
0240 NDRX_LOG(log_debug, "%s returns %d", __func__, ret);
0241
0242 return ret;
0243 }
0244
0245
0246
0247
0248
0249
0250 exprivate void rm_proc(exmemck_process_t *el)
0251 {
0252 EXHASH_DEL(M_proc, el);
0253
0254 NDRX_FREE(el->stats);
0255
0256 NDRX_FREE(el);
0257 }
0258
0259
0260
0261
0262
0263
0264
0265
0266 expublic int ndrx_memck_rm(char *mask)
0267 {
0268 int ret = EXSUCCEED;
0269 exmemck_config_t * cfg;
0270
0271 exmemck_process_t *el = NULL;
0272 exmemck_process_t *elt = NULL;
0273
0274 NDRX_LOG(log_debug, "%s enter, mask: [%s]", __func__, mask);
0275
0276 cfg = get_config(mask, EXFALSE, NULL, NULL);
0277
0278 if (NULL!=cfg)
0279 {
0280 NDRX_LOG(log_debug, "Entry found - removing...");
0281 EXHASH_DEL(M_config, cfg);
0282
0283 ndrx_regfree(&cfg->mask_regex);
0284
0285 if (cfg->neg_mask_used)
0286 {
0287 ndrx_regfree(&cfg->neg_mask_regex);
0288 }
0289
0290
0291
0292
0293 EXHASH_ITER(hh, M_proc, el, elt)
0294 {
0295 if (el->p_config == cfg)
0296 {
0297 NDRX_LOG(log_info, "deleting process: %d by mask [%s]",
0298 el->pid, mask);
0299 rm_proc(el);
0300 }
0301 }
0302
0303 NDRX_FREE(cfg);
0304 }
0305 out:
0306 return ret;
0307 }
0308
0309
0310
0311
0312 expublic void ndrx_memck_rmall(void)
0313 {
0314 exmemck_config_t * el, *elt;
0315
0316 EXHASH_ITER(hh, M_config, el, elt)
0317 {
0318 ndrx_memck_rm(el->mask);
0319 }
0320 }
0321
0322
0323
0324
0325
0326
0327 exprivate void calc_stat(exmemck_process_t *proc)
0328 {
0329 int ret = EXSUCCEED;
0330 int i;
0331 double start;
0332 int start_i;
0333 double stop;
0334 int stop_i;
0335 long rss;
0336 long vsz;
0337
0338 long diff_rss;
0339 long diff_vsz;
0340
0341 int first_halve_start;
0342 int second_halve_start;
0343 int second_halve_finish;
0344
0345 NDRX_LOG(log_debug, "%s: enter, pid=%d", __func__, proc->pid);
0346
0347
0348 start = (double)proc->nr_of_stats *
0349 ((double)proc->p_config->settings.interval_start_prcnt)/100.0f;
0350 stop = (double)proc->nr_of_stats *
0351 ((double)proc->p_config->settings.interval_stop_prcnt)/100.0f;
0352
0353 start_i = (int)start;
0354 stop_i = (int)stop;
0355
0356
0357 first_halve_start = start_i;
0358 second_halve_start = start_i + (stop_i - start_i)/2;
0359 second_halve_finish = stop_i;
0360
0361 if (first_halve_start == second_halve_start)
0362 {
0363 NDRX_LOG(log_debug, "No stats available for pid=%d (start==stop)", proc->pid);
0364 goto out;
0365 }
0366
0367
0368 rss = 0;
0369 vsz = 0;
0370
0371 NDRX_LOG(log_debug, "first halve loop [%d..%d]", first_halve_start,
0372 second_halve_start-1);
0373 proc->avg_first_count = 0;
0374 for (i=first_halve_start; i<second_halve_start; i++)
0375 {
0376 rss+=proc->stats[i].rss;
0377 vsz+=proc->stats[i].vsz;
0378 proc->avg_first_count++;
0379 }
0380
0381 proc->avg_first_halve_rss = rss / (second_halve_start - first_halve_start + 1);
0382 proc->avg_first_halve_vsz = vsz / (second_halve_start - first_halve_start + 1);
0383
0384
0385 rss = 0;
0386 vsz = 0;
0387
0388 NDRX_LOG(log_debug, "second halve loop [%d..%d]", second_halve_start,
0389 second_halve_finish-1);
0390 proc->avg_second_count = 0;
0391 for (i=second_halve_start; i<second_halve_finish; i++)
0392 {
0393 rss+=proc->stats[i].rss;
0394 vsz+=proc->stats[i].vsz;
0395 proc->avg_second_count++;
0396 }
0397
0398 if (proc->avg_first_count < proc->p_config->settings.min_values)
0399 {
0400 NDRX_LOG(log_info, "Too short of stats for first halve: %d, min: %d",
0401 proc->avg_first_count, proc->p_config->settings.min_values);
0402 goto out;
0403 }
0404
0405 if (proc->avg_second_count < proc->p_config->settings.min_values)
0406 {
0407 NDRX_LOG(log_info, "Too short of stats for second halve: %d, min: %d",
0408 proc->avg_second_count, proc->p_config->settings.min_values);
0409 goto out;
0410 }
0411
0412 proc->avg_second_halve_rss = rss / (second_halve_finish - second_halve_start + 1);
0413 proc->avg_second_halve_vsz = vsz / (second_halve_finish - second_halve_start + 1);
0414
0415 diff_rss = proc->avg_second_halve_rss - proc->avg_first_halve_rss;
0416 diff_vsz = proc->avg_second_halve_vsz - proc->avg_first_halve_vsz;
0417
0418
0419
0420 proc->status&=(~EXMEMCK_STATUS_LEAKY_RSS);
0421 proc->status&=(~EXMEMCK_STATUS_LEAKY_VSZ);
0422
0423
0424 proc->rss_increase_prcnt = ((double)diff_rss)/((double)proc->avg_second_halve_rss) * 100;
0425 proc->vsz_increase_prcnt = ((double)diff_vsz)/((double)proc->avg_second_halve_vsz) * 100;
0426
0427
0428 if ( proc->rss_increase_prcnt > (double)proc->p_config->settings.percent_diff_allow)
0429 {
0430 NDRX_LOG(log_warn, "pid %d leaky RSS: increase %lf%% allow: %d%%",
0431 proc->pid, proc->rss_increase_prcnt, proc->p_config->settings.percent_diff_allow);
0432 proc->status|=EXMEMCK_STATUS_LEAKY_RSS;
0433 }
0434
0435 if ( proc->vsz_increase_prcnt > (double)proc->p_config->settings.percent_diff_allow)
0436 {
0437 NDRX_LOG(log_warn, "pid %d leaky VSZ: increase %lf%% allow: %d%%",
0438 proc->pid, proc->vsz_increase_prcnt, proc->p_config->settings.percent_diff_allow);
0439 proc->status|=EXMEMCK_STATUS_LEAKY_VSZ;
0440 }
0441
0442 NDRX_LOG(log_debug, "Process %d avg stats, first halve 4k pages: rss=%ld, vsz=%ld "
0443 "second halve: rss=%ld, vsz=%ld, rss_diff=%ld, vsz_diff=%ld, rss incr %lf%%, "
0444 "vsz incr %lf%%, rss_leak=%s, vsz_leak=%s (ps: %s)",
0445 proc->pid,
0446 proc->avg_first_halve_rss, proc->avg_first_halve_vsz,
0447 proc->avg_second_halve_rss, proc->avg_second_halve_vsz,
0448 diff_rss, diff_vsz,
0449 proc->rss_increase_prcnt, proc->vsz_increase_prcnt,
0450 (proc->status&EXMEMCK_STATUS_LEAKY_RSS)?"yes":"no",
0451 (proc->status&EXMEMCK_STATUS_LEAKY_VSZ)?"yes":"no",
0452 proc->psout);
0453
0454
0455 if ((proc->status&EXMEMCK_STATUS_LEAKY_RSS) ||
0456 (proc->status&EXMEMCK_STATUS_LEAKY_VSZ))
0457 {
0458 NDRX_LOG(log_error, "Process leaky! Invoke callback if set -> [%s]",
0459 proc->psout);
0460 if (NULL!=proc->p_config->settings.pf_proc_leaky)
0461 {
0462 proc->p_config->settings.pf_proc_leaky(proc);
0463 }
0464 }
0465
0466 out:
0467 NDRX_LOG(log_debug, "%s: returns", __func__);
0468 }
0469
0470
0471
0472
0473
0474
0475 exprivate exmemck_process_t* get_proc(pid_t pid)
0476 {
0477 int pid_i = (int)pid;
0478 exmemck_process_t *ret=NULL;
0479
0480 EXHASH_FIND_INT(M_proc, &pid_i, ret);
0481
0482 return ret;
0483 }
0484
0485
0486
0487
0488
0489
0490 expublic exmemck_process_t* ndrx_memck_getstats_single(pid_t pid)
0491 {
0492 exmemck_process_t* ret;
0493
0494 if (NULL!=(ret = get_proc(pid)))
0495 {
0496 calc_stat(ret);
0497 }
0498
0499 return ret;
0500 }
0501
0502
0503
0504
0505
0506 expublic exmemck_process_t* ndrx_memck_getstats(void)
0507 {
0508
0509 exmemck_process_t* el, *elt;
0510
0511 NDRX_LOG(log_debug, "%s - enter", __func__);
0512
0513 EXHASH_ITER(hh, M_proc, el, elt)
0514 {
0515 calc_stat(el);
0516 }
0517
0518
0519 return M_proc;
0520 }
0521
0522
0523
0524
0525
0526
0527 expublic void ndrx_memck_reset(char *mask)
0528 {
0529 exmemck_config_t * cfg = get_config(mask, EXFALSE, NULL, NULL);
0530 exmemck_process_t *el, *elt;
0531
0532 if (NULL!=cfg)
0533 {
0534 EXHASH_ITER(hh, M_proc, el, elt)
0535 {
0536 if (el->p_config == cfg)
0537 {
0538
0539 NDRX_LOG(log_debug, "Resetting config for pid=%d (psout: [%s])",
0540 el->pid, el->psout);
0541
0542 NDRX_FREE(el->stats);
0543 el->nr_of_stats = 0;
0544 }
0545 }
0546 }
0547
0548 }
0549
0550
0551
0552
0553
0554
0555 expublic void ndrx_memck_reset_pid(pid_t pid)
0556 {
0557 exmemck_process_t* proc = get_proc(pid);
0558
0559 if (NULL!=proc)
0560 {
0561
0562 NDRX_LOG(log_debug, "Resetting config for pid=%d (psout: [%s])",
0563 proc->pid, proc->psout);
0564
0565 NDRX_FREE(proc->stats);
0566 proc->nr_of_stats = 0;
0567 }
0568 }
0569
0570
0571
0572
0573
0574 expublic int ndrx_memck_tick(void)
0575 {
0576 int ret = EXSUCCEED;
0577 exmemck_config_t *el, *elt;
0578
0579 string_list_t* sprocs = ndrx_sys_ps_list("", "", "", "", "");
0580 string_list_t* sproc;
0581 pid_t pid;
0582 exmemck_process_t* proc;
0583 exmemck_process_t* elp, *elpt;
0584 int is_new;
0585 ndrx_proc_info_t infos;
0586
0587 EXHASH_ITER(hh, M_proc, elp, elpt)
0588 {
0589 elp->proc_exists = EXFALSE;
0590 }
0591
0592 EXHASH_ITER(hh, M_config, el, elt)
0593 {
0594
0595 DL_FOREACH(sprocs, sproc)
0596 {
0597 if (EXSUCCEED==ndrx_regexec(&el->mask_regex, sproc->qname) &&
0598
0599
0600 ((!el->neg_mask_used) ||
0601 (EXSUCCEED!=ndrx_regexec(&el->neg_mask_regex, sproc->qname))))
0602 {
0603 NDRX_LOG(log_debug, "Process: [%s] matched for monitoring...",
0604 sproc->qname);
0605 if (EXSUCCEED!=ndrx_proc_pid_get_from_ps(sproc->qname, &pid))
0606 {
0607 NDRX_LOG(log_error, "Failed to extract pid from [%s]",
0608 sproc->qname);
0609 continue;
0610 }
0611
0612
0613
0614 if (pid==getpid())
0615 {
0616 NDRX_LOG(log_debug, "No self monitor...");
0617 continue;
0618 }
0619
0620 NDRX_LOG(log_debug, "got pid: [%d]", pid);
0621
0622 is_new = EXFALSE;
0623 proc = get_proc(pid);
0624
0625 if (NULL==proc)
0626 {
0627
0628
0629
0630
0631 NDRX_LOG(log_debug, "Process not found -> allocating");
0632
0633 if (NULL==(proc=NDRX_CALLOC(1, sizeof(exmemck_process_t))))
0634 {
0635 int err = errno;
0636 NDRX_LOG(log_error, "Failed calloc exmemck_process_t: %s",
0637 strerror(err));
0638
0639 userlog("Failed calloc exmemck_process_t: %s",
0640 strerror(err));
0641 EXFAIL_OUT(ret);
0642 }
0643 is_new = EXTRUE;
0644
0645 proc->pid = (int)pid;
0646 proc->p_config = el;
0647 NDRX_STRCPY_SAFE(proc->psout, sproc->qname);
0648 EXHASH_ADD_INT(M_proc, pid, proc);
0649 }
0650
0651 proc->proc_exists = EXTRUE;
0652
0653
0654 if (EXSUCCEED!=ndrx_proc_get_infos((pid_t)proc->pid, &infos))
0655 {
0656
0657 NDRX_LOG(log_warn, "Failed to get stats for pid %d",
0658 proc->pid);
0659 continue;
0660 }
0661
0662
0663 proc->nr_of_stats++;
0664 proc->stats = NDRX_REALLOC(proc->stats,
0665 proc->nr_of_stats*sizeof(*proc->stats));
0666
0667 if (NULL==proc->stats)
0668 {
0669 int err = errno;
0670
0671 NDRX_LOG(log_error, "Failed to alloc stats, size: %d: %s",
0672 proc->nr_of_stats*sizeof(*proc->stats), strerror(err));
0673
0674 userlog("Failed to alloc stats, size: %d: %s",
0675 proc->nr_of_stats*sizeof(*proc->stats), strerror(err));
0676
0677 EXFAIL_OUT(ret);
0678 }
0679
0680 proc->stats[proc->nr_of_stats-1].rss = infos.rss;
0681 proc->stats[proc->nr_of_stats-1].vsz = infos.vsz;
0682
0683 }
0684 }
0685 }
0686
0687
0688
0689
0690 EXHASH_ITER(hh, M_proc, elp, elpt)
0691 {
0692 if (!elp->proc_exists)
0693 {
0694 NDRX_LOG(log_warn, "Process pid=%d psout=[%s] - exited, generating stats",
0695 elp->pid, elp->psout);
0696 calc_stat(elp);
0697 rm_proc(elp);
0698 }
0699 }
0700
0701 out:
0702 if (NULL!=sprocs)
0703 {
0704 ndrx_string_list_free(sprocs);
0705 }
0706
0707 return ret;
0708 }
0709
0710