44import re
55from distutils .version import LooseVersion
66import mamonsu .lib .platform as platform
7+ import posix
78
89
910class MemoryLeakDiagnostic (Plugin ):
1011 DEFAULT_CONFIG = {'enabled' : 'False' ,
11- 'private_anon_mem_threshold' : '1GB' }
12+ 'private_anon_mem_threshold' : '1GB' ,
13+ 'interval' : '60' }
1214 Interval = 60
13-
15+
1416 query = 'select pid from pg_stat_activity'
1517 key_count_diff = 'pgsql.memory_leak_diagnostic.count_diff[]'
1618 key_count_diff_error = 'pgsql.memory_leak_diagnostic.msg_text[]'
1719 name_count_diff = 'PostgreSQL: number of pids which private anonymous memory exceeds ' \
1820 'private_anon_mem_threshold'
1921 name_count_diff_error = 'PostgreSQL: number of pids which private anonymous memory ' \
2022 'exceeds private_anon_mem_threshold, text of message'
21-
23+
2224 def __init__ (self , config ):
2325 super (Plugin , self ).__init__ (config )
2426 if not platform .LINUX :
2527 self .disable ()
2628 self .log .error ('Plugin {name} work only on Linux. ' .format (name = self .__class__ .__name__ ))
27-
29+
2830 if self .is_enabled ():
2931 self .page_size = os .sysconf ('SC_PAGE_SIZE' )
30-
32+
3133 private_anon_mem_threshold_row = self .plugin_config ('private_anon_mem_threshold' ).upper ()
3234 private_anon_mem_threshold , prefix = re .match (r'([0-9]*)([A-Z]*)' ,
3335 private_anon_mem_threshold_row , re .I ).groups ()
3436 ratio = 0
35-
37+
3638 if prefix == 'MB' :
3739 ratio = 1024 * 1024
3840 elif prefix == 'GB' :
@@ -44,17 +46,24 @@ def __init__(self, config):
4446 self .log .error ('Error in config, section [{section}], parameter private_anon_mem_threshold. '
4547 'Possible values MB, GB, TB. For example 1GB.'
4648 .format (section = self .__class__ .__name__ .lower ()))
47-
49+
4850 self .diff = ratio * int (private_anon_mem_threshold )
49-
50- self .os_release = os .uname ().release
51+
52+ uname = os .uname ()
53+ if isinstance (uname , tuple ):
54+ self .os_release = uname [2 ]
55+ elif isinstance (uname , posix .uname_result ):
56+ self .os_release = uname .release
57+ else :
58+ self .os_release = '0'
59+
5160 os_release_file = '/etc/os-release'
5261 try :
5362 release_file = open (os_release_file , 'r' ).readlines ()
5463 except Exception as e :
5564 self .log .info ('Cannot read file {os_release_file} : {e}' .format (os_release_file = os_release_file , e = e ))
5665 release_file = None
57-
66+
5867 if release_file :
5968 for line in release_file :
6069 if line .strip ('"\n ' ) != '' :
@@ -66,50 +75,47 @@ def __init__(self, config):
6675 else :
6776 self .os_name = None
6877 self .os_version = None
69-
78+
7079 def run (self , zbx ):
7180 pids = []
7281 count_diff = 0
7382 diffs = []
7483 msg_text = ''
75-
84+
7685 for row in Pooler .query (query = self .query ):
7786 pids .append (row [0 ])
78-
87+
7988 if (LooseVersion (self .os_release ) < LooseVersion ("4.5" )
80- and not (self .os_name == 'centos' and self .os_version == '7' ))\
89+ and not (self .os_name == 'centos' and self .os_version == '7' )) \
8190 or (not self .os_name and not self .os_version ):
8291 for pid in pids :
8392 try :
8493 statm = open ('/proc/{pid}/statm' .format (pid = pid ), 'r' ).read ().split (' ' )
8594 except FileNotFoundError :
8695 continue
87-
96+
8897 RES = int (statm [1 ]) * self .page_size
8998 SHR = int (statm [2 ]) * self .page_size
9099 if RES - SHR > self .diff :
91100 count_diff += 1
92101 diffs .append ({'pid' : pid , 'RES' : RES , 'SHR' : SHR , 'diff' : self .diff })
93102 if diffs :
94103 for diff in diffs :
95- msg_text += 'pid: {pid}, RES {RES} - SHR {SHR} more then {diff}\n ' .format (pid = diff .get ('pid' ),
96- RES = diff .get ('RES' ),
97- SHR = diff .get ('SHR' ),
98- diff = diff .get ('diff' ))
104+ msg_text += 'pid: {pid}, RES {RES} - SHR {SHR} more then {diff}\n ' .format_map (diff )
99105 else :
100106 for pid in pids :
101107 try :
102108 statm = open ('/proc/{pid}/status' .format (pid = pid ), 'r' ).readlines ()
103109 except FileNotFoundError :
104110 continue
105-
111+
106112 for line in statm :
107113 VmRSS = 0
108114 RssAnon = 0
109115 RssFile = 0
110116 RssShmem = 0
111117 k , v = line .split (':\t ' , 1 )
112-
118+
113119 if k == 'VmRSS' :
114120 VmRSS = int (v .strip ('"\n \t ' ).split (' ' )[0 ]) * 1024
115121 elif k == 'RssAnon' :
@@ -126,16 +132,11 @@ def run(self, zbx):
126132 if diffs :
127133 for diff in diffs :
128134 msg_text += 'pid: {pid}, RssAnon {RssAnon} more then {diff}, VmRSS {VmRSS}, ' \
129- 'RssFile {RssFile}, RssShmem {RssShmem} \n ' .format (pid = diff .get ('pid' ),
130- RssAnon = diff .get ('RssAnon' ),
131- diff = diff .get ('diff' ),
132- VmRSS = diff .get ('VmRSS' ),
133- RssFile = diff .get ('RssFile' ),
134- RssShmem = diff .get ('RssShmem' ))
135-
135+ 'RssFile {RssFile}, RssShmem {RssShmem} \n ' .format_map (diff )
136+
136137 zbx .send (self .key_count_diff , int (count_diff ))
137138 zbx .send (self .key_count_diff_error , msg_text )
138-
139+
139140 def items (self , template ):
140141 result = template .item (
141142 {
@@ -153,7 +154,7 @@ def items(self, template):
153154 }
154155 )
155156 return result
156-
157+
157158 def graphs (self , template ):
158159 result = template .graph (
159160 {
@@ -167,7 +168,7 @@ def graphs(self, template):
167168 }
168169 )
169170 return result
170-
171+
171172 def triggers (self , template ):
172173 result = template .trigger (
173174 {
0 commit comments