Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief Memory checking library
0003  *
0004  * @file exmemcklib.c
0005  */
0006 /* -----------------------------------------------------------------------------
0007  * Enduro/X Middleware Platform for Distributed Transaction Processing
0008  * Copyright (C) 2009-2016, ATR Baltic, Ltd. All Rights Reserved.
0009  * Copyright (C) 2017-2023, Mavimax, Ltd. All Rights Reserved.
0010  * This software is released under one of the following licenses:
0011  * AGPL (with Java and Go exceptions) or Mavimax's license for commercial use.
0012  * See LICENSE file for full text.
0013  * -----------------------------------------------------------------------------
0014  * AGPL license:
0015  *
0016  * This program is free software; you can redistribute it and/or modify it under
0017  * the terms of the GNU Affero General Public License, version 3 as published
0018  * by the Free Software Foundation;
0019  *
0020  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0021  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0022  * PARTICULAR PURPOSE. See the GNU Affero General Public License, version 3
0023  * for more details.
0024  *
0025  * You should have received a copy of the GNU Affero General Public License along 
0026  * with this program; if not, write to the Free Software Foundation, Inc.,
0027  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0028  *
0029  * -----------------------------------------------------------------------------
0030  * A commercial use license is available from Mavimax, Ltd
0031  * contact@mavimax.com
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 /*---------------------------Externs------------------------------------*/
0049 /*---------------------------Macros-------------------------------------*/
0050 #define MIN_STATS   2       /* min number of colleted RAM stats for halves */
0051 /*---------------------------Enums--------------------------------------*/
0052 /*---------------------------Typedefs-----------------------------------*/
0053 /*---------------------------Globals------------------------------------*/
0054 
0055 exmemck_config_t *M_config = NULL; /* global config */
0056 exmemck_process_t *M_proc = NULL; /* global process list */
0057 
0058 /*---------------------------Statics------------------------------------*/
0059 /*---------------------------Prototypes---------------------------------*/
0060 
0061 /**
0062  * Get config
0063  * @param mask
0064  * @param autocreate create entry automatically...
0065  * @return found/allocated config block or NULL
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         /* Allocate the block */
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  * Dump the configuration to log file
0121  * @param cfg configuration node
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  * Add configuration entry to the xm lib. Each entry will epply to the
0140  * list of the processes
0141  * @param mask    Mask key
0142  * @param dfld_mask Default mask
0143  * @param p_settings    Settings for the monitoring
0144  * @return SUCCEED/FAIL
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     /* search for defaults */
0165     if (is_new && NULL!=dlft_mask)
0166     {
0167         NDRX_LOG(log_debug, "Making init for defaults: [%s]", dlft_mask);
0168         
0169         /* ret will succeed here always! */
0170         dflt = get_config(dlft_mask, EXFALSE, &ret, NULL);
0171         
0172         if (NULL!=dflt)
0173         {
0174             /* Got defaults */
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         /* Compile the mask */
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  * Remove process
0247  * @param el
0248  * @return 
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  * Remove process by mask. Also will remove any monitored processes from the list
0261  * If mask is not found, then function will return SUCCEED anyway...
0262  * @param mask Mask to search for and remote, this will kill all the processes
0263  * monitored by this mask
0264  * @return SUCCEED/FAIL
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         /* Loop over the processes and kill with this config */
0291         
0292         /* Iterate over the hash! */
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  * Remove all masks...
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  * Calculate statistics for single process
0324  * if finding out that it is leaking, call the callback if defined.
0325  * Set the status...
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     /* read the statistics entry... */
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     /* first halve */
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     /* second halve */
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     /* recalc the flags */
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  * Lookup process by pid
0472  * @param pid
0473  * @return 
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  * Get statistics for single process
0487  * @param pid
0488  * @return 
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  * Return statistics blocks, linked list...
0504  * @return 
0505  */
0506 expublic exmemck_process_t* ndrx_memck_getstats(void)
0507 {
0508     /* calculate the stats and return... */    
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  * Reset statistics for process
0524  * @param mask
0525  * @return 
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                 /* Matched process, resetting */
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  * Reset statistics for the PID
0552  * @param pid
0553  * @return 
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         /* Matched process, resetting */
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  * Run the one cycle
0572  * @return 
0573  */
0574 expublic int ndrx_memck_tick(void)
0575 {
0576     int ret = EXSUCCEED;
0577     exmemck_config_t *el, *elt;
0578     /* List all processes */
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         /* Check them against monitoring table */
0595         DL_FOREACH(sprocs, sproc)
0596         {
0597             if (EXSUCCEED==ndrx_regexec(&el->mask_regex, sproc->qname) &&
0598                     /* So match if neg mask not used
0599                      * or if used and not matched... */
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                 /* if self proc -> continue not monitored... */
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                     /* Add process statistics, if found in table, 
0628                      * the add func shall create new
0629                      * entry or append existing entry 
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; /* Add config link.. */
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                 /* Read the memory entry */
0654                 if (EXSUCCEED!=ndrx_proc_get_infos((pid_t)proc->pid, &infos))
0655                 {
0656                     /* pid might be exited, thus ignore error */
0657                     NDRX_LOG(log_warn, "Failed to get stats for pid %d", 
0658                             proc->pid);
0659                     continue;
0660                 } 
0661                 
0662                 /* Append the statistics entry... */
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         } /* for each process... */
0685     }
0686     
0687     /* If process is not running anymore, just report its stats via callback
0688      * and remove from the memory
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 /* vim: set ts=4 sw=4 et smartindent: */