4
4
import re
5
5
from distutils .version import LooseVersion
6
6
import mamonsu .lib .platform as platform
7
+ import posix
7
8
8
9
9
10
class MemoryLeakDiagnostic (Plugin ):
10
11
DEFAULT_CONFIG = {'enabled' : 'False' ,
11
- 'private_anon_mem_threshold' : '1GB' }
12
+ 'private_anon_mem_threshold' : '1GB' ,
13
+ 'interval' : '60' }
12
14
Interval = 60
13
-
15
+
14
16
query = 'select pid from pg_stat_activity'
15
17
key_count_diff = 'pgsql.memory_leak_diagnostic.count_diff[]'
16
18
key_count_diff_error = 'pgsql.memory_leak_diagnostic.msg_text[]'
17
19
name_count_diff = 'PostgreSQL: number of pids which private anonymous memory exceeds ' \
18
20
'private_anon_mem_threshold'
19
21
name_count_diff_error = 'PostgreSQL: number of pids which private anonymous memory ' \
20
22
'exceeds private_anon_mem_threshold, text of message'
21
-
23
+
22
24
def __init__ (self , config ):
23
25
super (Plugin , self ).__init__ (config )
24
26
if not platform .LINUX :
25
27
self .disable ()
26
28
self .log .error ('Plugin {name} work only on Linux. ' .format (name = self .__class__ .__name__ ))
27
-
29
+
28
30
if self .is_enabled ():
29
31
self .page_size = os .sysconf ('SC_PAGE_SIZE' )
30
-
32
+
31
33
private_anon_mem_threshold_row = self .plugin_config ('private_anon_mem_threshold' ).upper ()
32
34
private_anon_mem_threshold , prefix = re .match (r'([0-9]*)([A-Z]*)' ,
33
35
private_anon_mem_threshold_row , re .I ).groups ()
34
36
ratio = 0
35
-
37
+
36
38
if prefix == 'MB' :
37
39
ratio = 1024 * 1024
38
40
elif prefix == 'GB' :
@@ -44,17 +46,24 @@ def __init__(self, config):
44
46
self .log .error ('Error in config, section [{section}], parameter private_anon_mem_threshold. '
45
47
'Possible values MB, GB, TB. For example 1GB.'
46
48
.format (section = self .__class__ .__name__ .lower ()))
47
-
49
+
48
50
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
+
51
60
os_release_file = '/etc/os-release'
52
61
try :
53
62
release_file = open (os_release_file , 'r' ).readlines ()
54
63
except Exception as e :
55
64
self .log .info ('Cannot read file {os_release_file} : {e}' .format (os_release_file = os_release_file , e = e ))
56
65
release_file = None
57
-
66
+
58
67
if release_file :
59
68
for line in release_file :
60
69
if line .strip ('"\n ' ) != '' :
@@ -66,50 +75,47 @@ def __init__(self, config):
66
75
else :
67
76
self .os_name = None
68
77
self .os_version = None
69
-
78
+
70
79
def run (self , zbx ):
71
80
pids = []
72
81
count_diff = 0
73
82
diffs = []
74
83
msg_text = ''
75
-
84
+
76
85
for row in Pooler .query (query = self .query ):
77
86
pids .append (row [0 ])
78
-
87
+
79
88
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' )) \
81
90
or (not self .os_name and not self .os_version ):
82
91
for pid in pids :
83
92
try :
84
93
statm = open ('/proc/{pid}/statm' .format (pid = pid ), 'r' ).read ().split (' ' )
85
94
except FileNotFoundError :
86
95
continue
87
-
96
+
88
97
RES = int (statm [1 ]) * self .page_size
89
98
SHR = int (statm [2 ]) * self .page_size
90
99
if RES - SHR > self .diff :
91
100
count_diff += 1
92
101
diffs .append ({'pid' : pid , 'RES' : RES , 'SHR' : SHR , 'diff' : self .diff })
93
102
if diffs :
94
103
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 )
99
105
else :
100
106
for pid in pids :
101
107
try :
102
108
statm = open ('/proc/{pid}/status' .format (pid = pid ), 'r' ).readlines ()
103
109
except FileNotFoundError :
104
110
continue
105
-
111
+
106
112
for line in statm :
107
113
VmRSS = 0
108
114
RssAnon = 0
109
115
RssFile = 0
110
116
RssShmem = 0
111
117
k , v = line .split (':\t ' , 1 )
112
-
118
+
113
119
if k == 'VmRSS' :
114
120
VmRSS = int (v .strip ('"\n \t ' ).split (' ' )[0 ]) * 1024
115
121
elif k == 'RssAnon' :
@@ -126,16 +132,11 @@ def run(self, zbx):
126
132
if diffs :
127
133
for diff in diffs :
128
134
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
+
136
137
zbx .send (self .key_count_diff , int (count_diff ))
137
138
zbx .send (self .key_count_diff_error , msg_text )
138
-
139
+
139
140
def items (self , template ):
140
141
result = template .item (
141
142
{
@@ -153,7 +154,7 @@ def items(self, template):
153
154
}
154
155
)
155
156
return result
156
-
157
+
157
158
def graphs (self , template ):
158
159
result = template .graph (
159
160
{
@@ -167,7 +168,7 @@ def graphs(self, template):
167
168
}
168
169
)
169
170
return result
170
-
171
+
171
172
def triggers (self , template ):
172
173
result = template .trigger (
173
174
{
0 commit comments