1209b8e3eSBram Moolenaar" Tests for memory usage. 2209b8e3eSBram Moolenaar 3209b8e3eSBram Moolenaarif !has('terminal') || has('gui_running') || $ASAN_OPTIONS !=# '' 4209b8e3eSBram Moolenaar " Skip tests on Travis CI ASAN build because it's difficult to estimate 5209b8e3eSBram Moolenaar " memory usage. 6209b8e3eSBram Moolenaar finish 7209b8e3eSBram Moolenaarendif 8209b8e3eSBram Moolenaar 9209b8e3eSBram Moolenaarsource shared.vim 10209b8e3eSBram Moolenaar 11209b8e3eSBram Moolenaarfunc s:pick_nr(str) abort 12209b8e3eSBram Moolenaar return substitute(a:str, '[^0-9]', '', 'g') * 1 13209b8e3eSBram Moolenaarendfunc 14209b8e3eSBram Moolenaar 15209b8e3eSBram Moolenaarif has('win32') 16209b8e3eSBram Moolenaar if !executable('wmic') 17209b8e3eSBram Moolenaar finish 18209b8e3eSBram Moolenaar endif 19209b8e3eSBram Moolenaar func s:memory_usage(pid) abort 20209b8e3eSBram Moolenaar let cmd = printf('wmic process where processid=%d get WorkingSetSize', a:pid) 21209b8e3eSBram Moolenaar return s:pick_nr(system(cmd)) / 1024 22209b8e3eSBram Moolenaar endfunc 23209b8e3eSBram Moolenaarelseif has('unix') 24209b8e3eSBram Moolenaar if !executable('ps') 25209b8e3eSBram Moolenaar finish 26209b8e3eSBram Moolenaar endif 27209b8e3eSBram Moolenaar func s:memory_usage(pid) abort 28209b8e3eSBram Moolenaar return s:pick_nr(system('ps -o rss= -p ' . a:pid)) 29209b8e3eSBram Moolenaar endfunc 30209b8e3eSBram Moolenaarelse 31209b8e3eSBram Moolenaar finish 32209b8e3eSBram Moolenaarendif 33209b8e3eSBram Moolenaar 34209b8e3eSBram Moolenaar" Wait for memory usage to level off. 35209b8e3eSBram Moolenaarfunc s:monitor_memory_usage(pid) abort 36209b8e3eSBram Moolenaar let proc = {} 37209b8e3eSBram Moolenaar let proc.pid = a:pid 38209b8e3eSBram Moolenaar let proc.hist = [] 39209b8e3eSBram Moolenaar let proc.min = 0 40209b8e3eSBram Moolenaar let proc.max = 0 41209b8e3eSBram Moolenaar 42209b8e3eSBram Moolenaar func proc.op() abort 43209b8e3eSBram Moolenaar " Check the last 200ms. 44209b8e3eSBram Moolenaar let val = s:memory_usage(self.pid) 45209b8e3eSBram Moolenaar if self.min > val 46209b8e3eSBram Moolenaar let self.min = val 47209b8e3eSBram Moolenaar elseif self.max < val 48209b8e3eSBram Moolenaar let self.max = val 49209b8e3eSBram Moolenaar endif 50209b8e3eSBram Moolenaar call add(self.hist, val) 51209b8e3eSBram Moolenaar if len(self.hist) < 20 52209b8e3eSBram Moolenaar return 0 53209b8e3eSBram Moolenaar endif 54209b8e3eSBram Moolenaar let sample = remove(self.hist, 0) 55209b8e3eSBram Moolenaar return len(uniq([sample] + self.hist)) == 1 56209b8e3eSBram Moolenaar endfunc 57209b8e3eSBram Moolenaar 58209b8e3eSBram Moolenaar call WaitFor({-> proc.op()}, 10000) 59209b8e3eSBram Moolenaar return {'last': get(proc.hist, -1), 'min': proc.min, 'max': proc.max} 60209b8e3eSBram Moolenaarendfunc 61209b8e3eSBram Moolenaar 62209b8e3eSBram Moolenaarlet s:term_vim = {} 63209b8e3eSBram Moolenaar 64209b8e3eSBram Moolenaarfunc s:term_vim.start(...) abort 65209b8e3eSBram Moolenaar let self.buf = term_start([GetVimProg()] + a:000) 66209b8e3eSBram Moolenaar let self.job = term_getjob(self.buf) 67209b8e3eSBram Moolenaar call WaitFor({-> job_status(self.job) ==# 'run'}) 68209b8e3eSBram Moolenaar let self.pid = job_info(self.job).process 69209b8e3eSBram Moolenaarendfunc 70209b8e3eSBram Moolenaar 71209b8e3eSBram Moolenaarfunc s:term_vim.stop() abort 72209b8e3eSBram Moolenaar call term_sendkeys(self.buf, ":qall!\<CR>") 73209b8e3eSBram Moolenaar call WaitFor({-> job_status(self.job) ==# 'dead'}) 74209b8e3eSBram Moolenaar exe self.buf . 'bwipe!' 75209b8e3eSBram Moolenaarendfunc 76209b8e3eSBram Moolenaar 77209b8e3eSBram Moolenaarfunc s:vim_new() abort 78209b8e3eSBram Moolenaar return copy(s:term_vim) 79209b8e3eSBram Moolenaarendfunc 80209b8e3eSBram Moolenaar 81209b8e3eSBram Moolenaarfunc Test_memory_func_capture_vargs() 82209b8e3eSBram Moolenaar " Case: if a local variable captures a:000, funccall object will be free 83209b8e3eSBram Moolenaar " just after it finishes. 84209b8e3eSBram Moolenaar let testfile = 'Xtest.vim' 85209b8e3eSBram Moolenaar call writefile([ 86209b8e3eSBram Moolenaar \ 'func s:f(...)', 87209b8e3eSBram Moolenaar \ ' let x = a:000', 88209b8e3eSBram Moolenaar \ 'endfunc', 89209b8e3eSBram Moolenaar \ 'for _ in range(10000)', 90209b8e3eSBram Moolenaar \ ' call s:f(0)', 91209b8e3eSBram Moolenaar \ 'endfor', 92209b8e3eSBram Moolenaar \ ], 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. 103209b8e3eSBram Moolenaar call assert_inrange(before, 2 * before, after.max) 104209b8e3eSBram Moolenaar " In this case, garbase collecting is not needed. 105209b8e3eSBram Moolenaar call assert_equal(after.last, after.max) 106209b8e3eSBram Moolenaar 107209b8e3eSBram Moolenaar call vim.stop() 108209b8e3eSBram Moolenaar call delete(testfile) 109209b8e3eSBram Moolenaarendfunc 110209b8e3eSBram Moolenaar 111209b8e3eSBram Moolenaarfunc Test_memory_func_capture_lvars() 112209b8e3eSBram Moolenaar " Case: if a local variable captures l: dict, funccall object will not be 113209b8e3eSBram Moolenaar " free until garbage collector runs, but after that memory usage doesn't 114209b8e3eSBram Moolenaar " increase so much even when rerun Xtest.vim since system memory caches. 115209b8e3eSBram Moolenaar let testfile = 'Xtest.vim' 116209b8e3eSBram Moolenaar call writefile([ 117209b8e3eSBram Moolenaar \ 'func s:f()', 118209b8e3eSBram Moolenaar \ ' let x = l:', 119209b8e3eSBram Moolenaar \ 'endfunc', 120209b8e3eSBram Moolenaar \ 'for _ in range(10000)', 121209b8e3eSBram Moolenaar \ ' call s:f()', 122209b8e3eSBram Moolenaar \ 'endfor', 123209b8e3eSBram Moolenaar \ ], testfile) 124209b8e3eSBram Moolenaar 125209b8e3eSBram Moolenaar let vim = s:vim_new() 126209b8e3eSBram Moolenaar call vim.start('--clean', '-c', 'set noswapfile', testfile) 127209b8e3eSBram Moolenaar let before = s:monitor_memory_usage(vim.pid).last 128209b8e3eSBram Moolenaar 129209b8e3eSBram Moolenaar call term_sendkeys(vim.buf, ":so %\<CR>") 130209b8e3eSBram Moolenaar call WaitFor({-> term_getcursor(vim.buf)[0] == 1}) 131209b8e3eSBram Moolenaar let after = s:monitor_memory_usage(vim.pid) 132209b8e3eSBram Moolenaar 133209b8e3eSBram Moolenaar " Rerun Xtest.vim. 134209b8e3eSBram Moolenaar for _ in range(3) 135209b8e3eSBram Moolenaar call term_sendkeys(vim.buf, ":so %\<CR>") 136209b8e3eSBram Moolenaar call WaitFor({-> term_getcursor(vim.buf)[0] == 1}) 137209b8e3eSBram Moolenaar let last = s:monitor_memory_usage(vim.pid).last 138209b8e3eSBram Moolenaar endfor 139209b8e3eSBram Moolenaar 140*08cda65dSBram Moolenaar " The usage may be a bit less than the last value 141*08cda65dSBram Moolenaar let lower = before * 8 / 10 142*08cda65dSBram Moolenaar call assert_inrange(lower, after.max + (after.last - before), last) 143209b8e3eSBram Moolenaar 144209b8e3eSBram Moolenaar call vim.stop() 145209b8e3eSBram Moolenaar call delete(testfile) 146209b8e3eSBram Moolenaarendfunc 147