OLD | NEW |
| (Empty) |
1 #!/usr/bin/env python | |
2 # | |
3 # $Id: test_memory_leaks.py 1142 2011-10-05 18:45:49Z g.rodola $ | |
4 # | |
5 # Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. | |
6 # Use of this source code is governed by a BSD-style license that can be | |
7 # found in the LICENSE file. | |
8 | |
9 """ | |
10 A test script which attempts to detect memory leaks by calling C | |
11 functions many times and compare process memory usage before and | |
12 after the calls. It might produce false positives. | |
13 """ | |
14 | |
15 import os | |
16 import gc | |
17 import unittest | |
18 import time | |
19 | |
20 import psutil | |
21 from test_psutil import reap_children, skipUnless, skipIf, \ | |
22 POSIX, LINUX, WINDOWS, OSX, BSD, PY3 | |
23 | |
24 LOOPS = 1000 | |
25 TOLERANCE = 4096 | |
26 | |
27 if PY3: | |
28 xrange = range | |
29 | |
30 | |
31 class Base(unittest.TestCase): | |
32 | |
33 def execute(self, function, *args, **kwargs): | |
34 # step 1 | |
35 for x in xrange(LOOPS): | |
36 self.call(function, *args, **kwargs) | |
37 del x | |
38 gc.collect() | |
39 rss1 = self.get_mem() | |
40 | |
41 # step 2 | |
42 for x in xrange(LOOPS): | |
43 self.call(function, *args, **kwargs) | |
44 del x | |
45 gc.collect() | |
46 rss2 = self.get_mem() | |
47 | |
48 # comparison | |
49 difference = rss2 - rss1 | |
50 if difference > TOLERANCE: | |
51 # This doesn't necessarily mean we have a leak yet. | |
52 # At this point we assume that after having called the | |
53 # function so many times the memory usage is stabilized | |
54 # and if there are no leaks it should not increase any | |
55 # more. | |
56 # Let's keep calling fun for 3 more seconds and fail if | |
57 # we notice any difference. | |
58 stop_at = time.time() + 3 | |
59 while 1: | |
60 self.call(function, *args, **kwargs) | |
61 if time.time() >= stop_at: | |
62 break | |
63 del stop_at | |
64 gc.collect() | |
65 rss3 = self.get_mem() | |
66 difference = rss3 - rss2 | |
67 if rss3 > rss2: | |
68 self.fail("rss2=%s, rss3=%s, difference=%s" \ | |
69 % (rss2, rss3, difference)) | |
70 | |
71 @staticmethod | |
72 def get_mem(): | |
73 return psutil.Process(os.getpid()).get_memory_info()[0] | |
74 | |
75 def call(self): | |
76 raise NotImplementedError("must be implemented in subclass") | |
77 | |
78 | |
79 class TestProcessObjectLeaks(Base): | |
80 """Test leaks of Process class methods and properties""" | |
81 | |
82 def setUp(self): | |
83 gc.collect() | |
84 | |
85 def tearDown(self): | |
86 reap_children() | |
87 | |
88 def call(self, function, *args, **kwargs): | |
89 p = psutil.Process(os.getpid()) | |
90 obj = getattr(p, function) | |
91 if callable(obj): | |
92 obj(*args, **kwargs) | |
93 | |
94 def test_name(self): | |
95 self.execute('name') | |
96 | |
97 def test_cmdline(self): | |
98 self.execute('cmdline') | |
99 | |
100 def test_ppid(self): | |
101 self.execute('ppid') | |
102 | |
103 @skipIf(WINDOWS) | |
104 def test_uids(self): | |
105 self.execute('uids') | |
106 | |
107 @skipIf(WINDOWS) | |
108 def test_gids(self): | |
109 self.execute('gids') | |
110 | |
111 def test_status(self): | |
112 self.execute('status') | |
113 | |
114 @skipIf(POSIX) | |
115 def test_username(self): | |
116 self.execute('username') | |
117 | |
118 def test_create_time(self): | |
119 self.execute('create_time') | |
120 | |
121 def test_get_num_threads(self): | |
122 self.execute('get_num_threads') | |
123 | |
124 def test_get_threads(self): | |
125 self.execute('get_threads') | |
126 | |
127 def test_get_cpu_times(self): | |
128 self.execute('get_cpu_times') | |
129 | |
130 def test_get_memory_info(self): | |
131 self.execute('get_memory_info') | |
132 | |
133 def test_is_running(self): | |
134 self.execute('is_running') | |
135 | |
136 @skipIf(WINDOWS) | |
137 def test_terminal(self): | |
138 self.execute('terminal') | |
139 | |
140 @skipUnless(WINDOWS) | |
141 def test_resume(self): | |
142 self.execute('resume') | |
143 | |
144 @skipUnless(WINDOWS) | |
145 def test_getcwd(self): | |
146 self.execute('getcwd') | |
147 | |
148 @skipUnless(WINDOWS or OSX) | |
149 def test_get_open_files(self): | |
150 self.execute('get_open_files') | |
151 | |
152 @skipUnless(WINDOWS or OSX) | |
153 def test_get_connections(self): | |
154 self.execute('get_connections') | |
155 | |
156 | |
157 class TestModuleFunctionsLeaks(Base): | |
158 """Test leaks of psutil module functions.""" | |
159 | |
160 def setUp(self): | |
161 gc.collect() | |
162 | |
163 def call(self, function, *args, **kwargs): | |
164 obj = getattr(psutil, function) | |
165 if callable(obj): | |
166 retvalue = obj(*args, **kwargs) | |
167 | |
168 def test_get_pid_list(self): | |
169 self.execute('get_pid_list') | |
170 | |
171 @skipIf(POSIX) | |
172 def test_pid_exists(self): | |
173 self.execute('pid_exists', os.getpid()) | |
174 | |
175 def test_process_iter(self): | |
176 self.execute('process_iter') | |
177 | |
178 def test_phymem_usage(self): | |
179 self.execute('phymem_usage') | |
180 | |
181 def test_virtmem_usage(self): | |
182 self.execute('virtmem_usage') | |
183 | |
184 def test_cpu_times(self): | |
185 self.execute('cpu_times') | |
186 | |
187 def test_per_cpu_times(self): | |
188 self.execute('cpu_times', percpu=True) | |
189 | |
190 @skipUnless(WINDOWS) | |
191 def test_disk_usage(self): | |
192 self.execute('disk_usage', '.') | |
193 | |
194 def test_disk_partitions(self): | |
195 self.execute('disk_partitions') | |
196 | |
197 def test_network_io_counters(self): | |
198 self.execute('network_io_counters') | |
199 | |
200 def test_disk_io_counters(self): | |
201 self.execute('disk_io_counters') | |
202 | |
203 def test_main(): | |
204 test_suite = unittest.TestSuite() | |
205 test_suite.addTest(unittest.makeSuite(TestProcessObjectLeaks)) | |
206 test_suite.addTest(unittest.makeSuite(TestModuleFunctionsLeaks)) | |
207 unittest.TextTestRunner(verbosity=2).run(test_suite) | |
208 | |
209 if __name__ == '__main__': | |
210 test_main() | |
211 | |
OLD | NEW |