OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # | 2 # |
3 # $Id: test_memory_leaks.py 777 2010-11-08 18:29:13Z g.rodola $ | 3 # $Id: test_memory_leaks.py 1142 2011-10-05 18:45:49Z g.rodola $ |
4 # | 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. |
5 | 8 |
6 """ | 9 """ |
7 Note: this is targeted for python 2.x. | 10 A test script which attempts to detect memory leaks by calling C |
8 To run it under python 3.x you need to use 2to3 tool first: | 11 functions many times and compare process memory usage before and |
9 | 12 after the calls. It might produce false positives. |
10 $ 2to3 -w test/test_memory_leaks.py | |
11 """ | 13 """ |
12 | 14 |
13 | |
14 import os | 15 import os |
15 import gc | 16 import gc |
16 import sys | |
17 import unittest | 17 import unittest |
| 18 import time |
18 | 19 |
19 import psutil | 20 import psutil |
20 from test_psutil import reap_children, skipUnless, skipIf, \ | 21 from test_psutil import reap_children, skipUnless, skipIf, \ |
21 POSIX, LINUX, WINDOWS, OSX, BSD | 22 POSIX, LINUX, WINDOWS, OSX, BSD, PY3 |
22 | 23 |
23 LOOPS = 1000 | 24 LOOPS = 1000 |
24 TOLERANCE = 4096 | 25 TOLERANCE = 4096 |
25 | 26 |
| 27 if PY3: |
| 28 xrange = range |
26 | 29 |
27 class TestProcessObjectLeaks(unittest.TestCase): | 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): |
28 """Test leaks of Process class methods and properties""" | 80 """Test leaks of Process class methods and properties""" |
29 | 81 |
30 def setUp(self): | 82 def setUp(self): |
31 gc.collect() | 83 gc.collect() |
32 | 84 |
33 def tearDown(self): | 85 def tearDown(self): |
34 reap_children() | 86 reap_children() |
35 | 87 |
36 def execute(self, method, *args, **kwarks): | 88 def call(self, function, *args, **kwargs): |
37 # step 1 | |
38 p = psutil.Process(os.getpid()) | 89 p = psutil.Process(os.getpid()) |
39 for x in xrange(LOOPS): | 90 obj = getattr(p, function) |
40 obj = getattr(p, method) | 91 if callable(obj): |
41 if callable(obj): | 92 obj(*args, **kwargs) |
42 retvalue = obj(*args, **kwarks) | |
43 else: | |
44 retvalue = obj # property | |
45 del x, p, obj, retvalue | |
46 gc.collect() | |
47 rss1 = psutil.Process(os.getpid()).get_memory_info()[0] | |
48 | |
49 # step 2 | |
50 p = psutil.Process(os.getpid()) | |
51 for x in xrange(LOOPS): | |
52 obj = getattr(p, method) | |
53 if callable(obj): | |
54 retvalue = obj(*args, **kwarks) | |
55 else: | |
56 retvalue = obj # property | |
57 del x, p, obj, retvalue | |
58 gc.collect() | |
59 rss2 = psutil.Process(os.getpid()).get_memory_info()[0] | |
60 | |
61 # comparison | |
62 difference = rss2 - rss1 | |
63 if difference > TOLERANCE: | |
64 self.fail("rss1=%s, rss2=%s, difference=%s" %(rss1, rss2, difference
)) | |
65 | 93 |
66 def test_name(self): | 94 def test_name(self): |
67 self.execute('name') | 95 self.execute('name') |
68 | 96 |
69 def test_cmdline(self): | 97 def test_cmdline(self): |
70 self.execute('cmdline') | 98 self.execute('cmdline') |
71 | 99 |
72 def test_ppid(self): | 100 def test_ppid(self): |
73 self.execute('ppid') | 101 self.execute('ppid') |
74 | 102 |
75 def test_uid(self): | 103 @skipIf(WINDOWS) |
76 self.execute('uid') | 104 def test_uids(self): |
| 105 self.execute('uids') |
77 | 106 |
78 def test_uid(self): | 107 @skipIf(WINDOWS) |
79 self.execute('gid') | 108 def test_gids(self): |
| 109 self.execute('gids') |
| 110 |
| 111 def test_status(self): |
| 112 self.execute('status') |
80 | 113 |
81 @skipIf(POSIX) | 114 @skipIf(POSIX) |
82 def test_username(self): | 115 def test_username(self): |
83 self.execute('username') | 116 self.execute('username') |
84 | 117 |
85 def test_create_time(self): | 118 def test_create_time(self): |
86 self.execute('create_time') | 119 self.execute('create_time') |
87 | 120 |
88 def test_get_num_threads(self): | 121 def test_get_num_threads(self): |
89 self.execute('get_num_threads') | 122 self.execute('get_num_threads') |
90 | 123 |
| 124 def test_get_threads(self): |
| 125 self.execute('get_threads') |
| 126 |
91 def test_get_cpu_times(self): | 127 def test_get_cpu_times(self): |
92 self.execute('get_cpu_times') | 128 self.execute('get_cpu_times') |
93 | 129 |
94 def test_get_memory_info(self): | 130 def test_get_memory_info(self): |
95 self.execute('get_memory_info') | 131 self.execute('get_memory_info') |
96 | 132 |
97 def test_is_running(self): | 133 def test_is_running(self): |
98 self.execute('is_running') | 134 self.execute('is_running') |
99 | 135 |
| 136 @skipIf(WINDOWS) |
| 137 def test_terminal(self): |
| 138 self.execute('terminal') |
| 139 |
100 @skipUnless(WINDOWS) | 140 @skipUnless(WINDOWS) |
101 def test_resume(self): | 141 def test_resume(self): |
102 self.execute('resume') | 142 self.execute('resume') |
103 | 143 |
104 @skipUnless(WINDOWS) | 144 @skipUnless(WINDOWS) |
105 def test_getcwd(self): | 145 def test_getcwd(self): |
106 self.execute('getcwd') | 146 self.execute('getcwd') |
107 | 147 |
108 @skipUnless(WINDOWS) | 148 @skipUnless(WINDOWS or OSX) |
109 def test_get_open_files(self): | 149 def test_get_open_files(self): |
110 self.execute('get_open_files') | 150 self.execute('get_open_files') |
111 | 151 |
112 @skipUnless(WINDOWS) | 152 @skipUnless(WINDOWS or OSX) |
113 def test_get_connections(self): | 153 def test_get_connections(self): |
114 self.execute('get_connections') | 154 self.execute('get_connections') |
115 | 155 |
116 | 156 |
117 class TestModuleFunctionsLeaks(unittest.TestCase): | 157 class TestModuleFunctionsLeaks(Base): |
118 """Test leaks of psutil module functions.""" | 158 """Test leaks of psutil module functions.""" |
119 | 159 |
120 def setUp(self): | 160 def setUp(self): |
121 gc.collect() | 161 gc.collect() |
122 | 162 |
123 def execute(self, function, *args, **kwarks): | 163 def call(self, function, *args, **kwargs): |
124 # step 1 | 164 obj = getattr(psutil, function) |
125 for x in xrange(LOOPS): | 165 if callable(obj): |
126 obj = getattr(psutil, function) | 166 retvalue = obj(*args, **kwargs) |
127 if callable(obj): | |
128 retvalue = obj(*args, **kwarks) | |
129 else: | |
130 retvalue = obj # property | |
131 del x, obj, retvalue | |
132 gc.collect() | |
133 rss1 = psutil.Process(os.getpid()).get_memory_info()[0] | |
134 | |
135 # step 2 | |
136 for x in xrange(LOOPS): | |
137 obj = getattr(psutil, function) | |
138 if callable(obj): | |
139 retvalue = obj(*args, **kwarks) | |
140 else: | |
141 retvalue = obj # property | |
142 del x, obj, retvalue | |
143 gc.collect() | |
144 rss2 = psutil.Process(os.getpid()).get_memory_info()[0] | |
145 | |
146 # comparison | |
147 difference = rss2 - rss1 | |
148 if difference > TOLERANCE: | |
149 self.fail("rss1=%s, rss2=%s, difference=%s" %(rss1, rss2, difference
)) | |
150 | 167 |
151 def test_get_pid_list(self): | 168 def test_get_pid_list(self): |
152 self.execute('get_pid_list') | 169 self.execute('get_pid_list') |
153 | 170 |
154 @skipIf(POSIX) | 171 @skipIf(POSIX) |
155 def test_pid_exists(self): | 172 def test_pid_exists(self): |
156 self.execute('pid_exists', os.getpid()) | 173 self.execute('pid_exists', os.getpid()) |
157 | 174 |
158 def test_process_iter(self): | 175 def test_process_iter(self): |
159 self.execute('process_iter') | 176 self.execute('process_iter') |
160 | 177 |
161 def test_used_phymem(self): | 178 def test_phymem_usage(self): |
162 self.execute('used_phymem') | 179 self.execute('phymem_usage') |
163 | 180 |
164 def test_avail_phymem(self): | 181 def test_virtmem_usage(self): |
165 self.execute('avail_phymem') | 182 self.execute('virtmem_usage') |
166 | |
167 def test_total_virtmem(self): | |
168 self.execute('total_virtmem') | |
169 | |
170 def test_used_virtmem(self): | |
171 self.execute('used_virtmem') | |
172 | |
173 def test_avail_virtmem(self): | |
174 self.execute('avail_virtmem') | |
175 | 183 |
176 def test_cpu_times(self): | 184 def test_cpu_times(self): |
177 self.execute('cpu_times') | 185 self.execute('cpu_times') |
178 | 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') |
179 | 202 |
180 def test_main(): | 203 def test_main(): |
181 test_suite = unittest.TestSuite() | 204 test_suite = unittest.TestSuite() |
182 test_suite.addTest(unittest.makeSuite(TestProcessObjectLeaks)) | 205 test_suite.addTest(unittest.makeSuite(TestProcessObjectLeaks)) |
183 test_suite.addTest(unittest.makeSuite(TestModuleFunctionsLeaks)) | 206 test_suite.addTest(unittest.makeSuite(TestModuleFunctionsLeaks)) |
184 unittest.TextTestRunner(verbosity=2).run(test_suite) | 207 unittest.TextTestRunner(verbosity=2).run(test_suite) |
185 | 208 |
186 if __name__ == '__main__': | 209 if __name__ == '__main__': |
187 test_main() | 210 test_main() |
188 | 211 |
OLD | NEW |