1209b8e3eSBram Moolenaar" Tests for memory usage. 2209b8e3eSBram Moolenaar 3b46fecd3SBram Moolenaarsource check.vim 4b46fecd3SBram MoolenaarCheckFeature terminal 58c5a278fSBram MoolenaarCheckNotGui 6b46fecd3SBram Moolenaar 797202d95SBram Moolenaar" Skip tests on Travis CI ASAN build because it's difficult to estimate memory 897202d95SBram Moolenaar" usage. 997202d95SBram MoolenaarCheckNotAsan 10209b8e3eSBram Moolenaar 11209b8e3eSBram Moolenaarsource shared.vim 12209b8e3eSBram Moolenaar 13209b8e3eSBram Moolenaarfunc s:pick_nr(str) abort 14209b8e3eSBram Moolenaar return substitute(a:str, '[^0-9]', '', 'g') * 1 15209b8e3eSBram Moolenaarendfunc 16209b8e3eSBram Moolenaar 17209b8e3eSBram Moolenaarif has('win32') 18209b8e3eSBram Moolenaar if !executable('wmic') 19b46fecd3SBram Moolenaar throw 'Skipped: wmic program missing' 20209b8e3eSBram Moolenaar endif 21209b8e3eSBram Moolenaar func s:memory_usage(pid) abort 22209b8e3eSBram Moolenaar let cmd = printf('wmic process where processid=%d get WorkingSetSize', a:pid) 23209b8e3eSBram Moolenaar return s:pick_nr(system(cmd)) / 1024 24209b8e3eSBram Moolenaar endfunc 25209b8e3eSBram Moolenaarelseif has('unix') 26209b8e3eSBram Moolenaar if !executable('ps') 27b46fecd3SBram Moolenaar throw 'Skipped: ps program missing' 28209b8e3eSBram Moolenaar endif 29209b8e3eSBram Moolenaar func s:memory_usage(pid) abort 30209b8e3eSBram Moolenaar return s:pick_nr(system('ps -o rss= -p ' . a:pid)) 31209b8e3eSBram Moolenaar endfunc 32209b8e3eSBram Moolenaarelse 33b46fecd3SBram Moolenaar throw 'Skipped: not win32 or unix' 34209b8e3eSBram Moolenaarendif 35209b8e3eSBram Moolenaar 36209b8e3eSBram Moolenaar" Wait for memory usage to level off. 37209b8e3eSBram Moolenaarfunc s:monitor_memory_usage(pid) abort 38209b8e3eSBram Moolenaar let proc = {} 39209b8e3eSBram Moolenaar let proc.pid = a:pid 40209b8e3eSBram Moolenaar let proc.hist = [] 41209b8e3eSBram Moolenaar let proc.max = 0 42209b8e3eSBram Moolenaar 43209b8e3eSBram Moolenaar func proc.op() abort 44209b8e3eSBram Moolenaar " Check the last 200ms. 45209b8e3eSBram Moolenaar let val = s:memory_usage(self.pid) 46f7e47af7SBram Moolenaar if self.max < val 47209b8e3eSBram Moolenaar let self.max = val 48209b8e3eSBram Moolenaar endif 49209b8e3eSBram Moolenaar call add(self.hist, val) 50209b8e3eSBram Moolenaar if len(self.hist) < 20 51209b8e3eSBram Moolenaar return 0 52209b8e3eSBram Moolenaar endif 53209b8e3eSBram Moolenaar let sample = remove(self.hist, 0) 54209b8e3eSBram Moolenaar return len(uniq([sample] + self.hist)) == 1 55209b8e3eSBram Moolenaar endfunc 56209b8e3eSBram Moolenaar 57209b8e3eSBram Moolenaar call WaitFor({-> proc.op()}, 10000) 58f7e47af7SBram Moolenaar return {'last': get(proc.hist, -1), 'max': proc.max} 59209b8e3eSBram Moolenaarendfunc 60209b8e3eSBram Moolenaar 61209b8e3eSBram Moolenaarlet s:term_vim = {} 62209b8e3eSBram Moolenaar 63209b8e3eSBram Moolenaarfunc s:term_vim.start(...) abort 64209b8e3eSBram Moolenaar let self.buf = term_start([GetVimProg()] + a:000) 65209b8e3eSBram Moolenaar let self.job = term_getjob(self.buf) 66209b8e3eSBram Moolenaar call WaitFor({-> job_status(self.job) ==# 'run'}) 67209b8e3eSBram Moolenaar let self.pid = job_info(self.job).process 68209b8e3eSBram Moolenaarendfunc 69209b8e3eSBram Moolenaar 70209b8e3eSBram Moolenaarfunc s:term_vim.stop() abort 71209b8e3eSBram Moolenaar call term_sendkeys(self.buf, ":qall!\<CR>") 72209b8e3eSBram Moolenaar call WaitFor({-> job_status(self.job) ==# 'dead'}) 73209b8e3eSBram Moolenaar exe self.buf . 'bwipe!' 74209b8e3eSBram Moolenaarendfunc 75209b8e3eSBram Moolenaar 76209b8e3eSBram Moolenaarfunc s:vim_new() abort 77209b8e3eSBram Moolenaar return copy(s:term_vim) 78209b8e3eSBram Moolenaarendfunc 79209b8e3eSBram Moolenaar 80209b8e3eSBram Moolenaarfunc Test_memory_func_capture_vargs() 81209b8e3eSBram Moolenaar " Case: if a local variable captures a:000, funccall object will be free 82209b8e3eSBram Moolenaar " just after it finishes. 83209b8e3eSBram Moolenaar let testfile = 'Xtest.vim' 84e7eb9270SBram Moolenaar let lines =<< trim END 85e7eb9270SBram Moolenaar func s:f(...) 86e7eb9270SBram Moolenaar let x = a:000 87e7eb9270SBram Moolenaar endfunc 88e7eb9270SBram Moolenaar for _ in range(10000) 89e7eb9270SBram Moolenaar call s:f(0) 90e7eb9270SBram Moolenaar endfor 91e7eb9270SBram Moolenaar END 92e7eb9270SBram Moolenaar call writefile(lines, testfile) 93209b8e3eSBram Moolenaar 94209b8e3eSBram Moolenaar let vim = s:vim_new() 95209b8e3eSBram Moolenaar call vim.start('--clean', '-c', 'set noswapfile', testfile) 96209b8e3eSBram Moolenaar let before = s:monitor_memory_usage(vim.pid).last 97209b8e3eSBram Moolenaar 98209b8e3eSBram Moolenaar call term_sendkeys(vim.buf, ":so %\<CR>") 99209b8e3eSBram Moolenaar call WaitFor({-> term_getcursor(vim.buf)[0] == 1}) 100209b8e3eSBram Moolenaar let after = s:monitor_memory_usage(vim.pid) 101209b8e3eSBram Moolenaar 102209b8e3eSBram Moolenaar " Estimate the limit of max usage as 2x initial usage. 1035d508dd3SBram Moolenaar " The lower limit can fluctuate a bit, use 97%. 1045d508dd3SBram Moolenaar call assert_inrange(before * 97 / 100, 2 * before, after.max) 1053a731ee0SBram Moolenaar 1063a731ee0SBram Moolenaar " In this case, garbage collecting is not needed. 1075d508dd3SBram Moolenaar " The value might fluctuate a bit, allow for 3% tolerance below and 5% above. 1085d508dd3SBram Moolenaar " Based on various test runs. 109f7e47af7SBram Moolenaar let lower = after.last * 97 / 100 1105d508dd3SBram Moolenaar let upper = after.last * 105 / 100 111f7e47af7SBram Moolenaar call assert_inrange(lower, upper, after.max) 112209b8e3eSBram Moolenaar 113209b8e3eSBram Moolenaar call vim.stop() 114209b8e3eSBram Moolenaar call delete(testfile) 115209b8e3eSBram Moolenaarendfunc 116209b8e3eSBram Moolenaar 117209b8e3eSBram Moolenaarfunc Test_memory_func_capture_lvars() 118209b8e3eSBram Moolenaar " Case: if a local variable captures l: dict, funccall object will not be 119209b8e3eSBram Moolenaar " free until garbage collector runs, but after that memory usage doesn't 120209b8e3eSBram Moolenaar " increase so much even when rerun Xtest.vim since system memory caches. 121209b8e3eSBram Moolenaar let testfile = 'Xtest.vim' 122e7eb9270SBram Moolenaar let lines =<< trim END 123e7eb9270SBram Moolenaar func s:f() 124e7eb9270SBram Moolenaar let x = l: 125e7eb9270SBram Moolenaar endfunc 126e7eb9270SBram Moolenaar for _ in range(10000) 127e7eb9270SBram Moolenaar call s:f() 128e7eb9270SBram Moolenaar endfor 129e7eb9270SBram Moolenaar END 130e7eb9270SBram Moolenaar call writefile(lines, testfile) 131209b8e3eSBram Moolenaar 132209b8e3eSBram Moolenaar let vim = s:vim_new() 133209b8e3eSBram Moolenaar call vim.start('--clean', '-c', 'set noswapfile', testfile) 134209b8e3eSBram Moolenaar let before = s:monitor_memory_usage(vim.pid).last 135209b8e3eSBram Moolenaar 136209b8e3eSBram Moolenaar call term_sendkeys(vim.buf, ":so %\<CR>") 137209b8e3eSBram Moolenaar call WaitFor({-> term_getcursor(vim.buf)[0] == 1}) 138209b8e3eSBram Moolenaar let after = s:monitor_memory_usage(vim.pid) 139209b8e3eSBram Moolenaar 140209b8e3eSBram Moolenaar " Rerun Xtest.vim. 141209b8e3eSBram Moolenaar for _ in range(3) 142209b8e3eSBram Moolenaar call term_sendkeys(vim.buf, ":so %\<CR>") 143209b8e3eSBram Moolenaar call WaitFor({-> term_getcursor(vim.buf)[0] == 1}) 144209b8e3eSBram Moolenaar let last = s:monitor_memory_usage(vim.pid).last 145209b8e3eSBram Moolenaar endfor 146209b8e3eSBram Moolenaar 147f7e47af7SBram Moolenaar " The usage may be a bit less than the last value, use 80%. 1486b6f7aaeSBram Moolenaar " Allow for 20% tolerance at the upper limit. That's very permissive, but 1491832d12aSBram Moolenaar " otherwise the test fails sometimes. On Cirrus CI with FreeBSD we need to 150*6bce5856SBram Moolenaar " be even much more permissive. 1511832d12aSBram Moolenaar if has('bsd') 152*6bce5856SBram Moolenaar let multiplier = 19 1531832d12aSBram Moolenaar else 1541832d12aSBram Moolenaar let multiplier = 12 1551832d12aSBram Moolenaar endif 15608cda65dSBram Moolenaar let lower = before * 8 / 10 1571832d12aSBram Moolenaar let upper = (after.max + (after.last - before)) * multiplier / 10 158f7e47af7SBram Moolenaar call assert_inrange(lower, upper, last) 159209b8e3eSBram Moolenaar 160209b8e3eSBram Moolenaar call vim.stop() 161209b8e3eSBram Moolenaar call delete(testfile) 162209b8e3eSBram Moolenaarendfunc 1636d91bcb4SBram Moolenaar 1646d91bcb4SBram Moolenaar" vim: shiftwidth=2 sts=2 expandtab 165