| 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 |