mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-01-23 10:30:05 +08:00
Add profile plugin (#3290)
This commit is contained in:
parent
5d6df904a3
commit
8d294a09bd
371
.SpaceVim.d/autoload/SpaceVim/dev/profile.vim
Normal file
371
.SpaceVim.d/autoload/SpaceVim/dev/profile.vim
Normal file
@ -0,0 +1,371 @@
|
||||
"=============================================================================
|
||||
" profile.vim --- Profile startuptime
|
||||
" Copyright (c) 2016-2019 Wang Shidong & Contributors
|
||||
" Author: Wang Shidong < wsdjeg@outlook.com >
|
||||
" URL: https://spacevim.org
|
||||
" License: GPLv3
|
||||
"=============================================================================
|
||||
|
||||
let s:CPT = SpaceVim#api#import('vim#compatible')
|
||||
|
||||
let s:plugin_hints = [
|
||||
\ 'autoload',
|
||||
\ 'colors',
|
||||
\ 'compiler',
|
||||
\ 'filetype.vim',
|
||||
\ 'ftdetect',
|
||||
\ 'ftplugin',
|
||||
\ 'indent',
|
||||
\ 'keymap',
|
||||
\ 'plugin',
|
||||
\ 'rplugin',
|
||||
\ 'syntax',
|
||||
\ ]
|
||||
|
||||
let s:levels = [
|
||||
\ 'Flawless Victory',
|
||||
\ 'Outstanding',
|
||||
\ 'Fatality',
|
||||
\ 'Toasty',
|
||||
\ 'Impressive',
|
||||
\ 'Well Done',
|
||||
\ 'Test your might',
|
||||
\ ]
|
||||
|
||||
|
||||
function! s:plugin_sort(a, b) abort
|
||||
return len(a:b[0]) - len(a:a[0])
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:result_sort(a, b) abort
|
||||
if a:a[1] < a:b[1]
|
||||
return 1
|
||||
elseif a:a[1] > a:b[1]
|
||||
return -1
|
||||
endif
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:get_vimrc() abort
|
||||
if exists('$MYVIMRC') && !empty($MYVIMRC)
|
||||
return expand($MYVIMRC)
|
||||
endif
|
||||
|
||||
return expand(matchstr(s:CPT.execute('silent scriptnames'), '1: \zs\f\+'))
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:init_plugins(vimrc) abort
|
||||
let vimrc_path = substitute(fnamemodify(a:vimrc, ':p:h') . '/',
|
||||
\ '//', '/', 'g')
|
||||
let default_vimrc_path = fnamemodify('~/.vim', ':p')
|
||||
let runtime_path = fnamemodify(expand('$VIMRUNTIME'), ':p')
|
||||
let nvim_config = substitute((exists('$XDG_CONFIG_HOME')
|
||||
\ ? expand('$XDG_CONFIG_HOME') : fnamemodify('~/.config', ':p'))
|
||||
\ . '/nvim/', '//', '/', 'g')
|
||||
let home = fnamemodify('~', ':p')
|
||||
let seen = [vimrc_path, default_vimrc_path, runtime_path, nvim_config]
|
||||
let s:plugins = []
|
||||
|
||||
if exists('g:plugs')
|
||||
for [plugin, info] in items(g:plugs)
|
||||
if !has_key(info, 'dir') || index(seen, info.dir) != -1
|
||||
continue
|
||||
endif
|
||||
call add(s:plugins, [info.dir, plugin])
|
||||
call add(seen, info.dir)
|
||||
endfor
|
||||
elseif exists('g:dein#_plugins')
|
||||
for [plugin, info] in items(g:dein#_plugins)
|
||||
if !has_key(info, 'rtp') || index(seen, info.rtp) != -1
|
||||
continue
|
||||
endif
|
||||
let dir = fnamemodify(info.rtp, ':p')
|
||||
call add(s:plugins, [dir, plugin])
|
||||
call add(seen, dir)
|
||||
endfor
|
||||
endif
|
||||
|
||||
for path in split(&runtimepath, ',')
|
||||
let path = fnamemodify(path, ':p')
|
||||
if path =~# '/$'
|
||||
let path = path[:-2]
|
||||
endif
|
||||
|
||||
if path =~# '/after$'
|
||||
let path = fnamemodify(path, ':h')
|
||||
if path =~# '/$'
|
||||
let path = path[:-2]
|
||||
endif
|
||||
endif
|
||||
|
||||
if isdirectory(path) && index(seen, path . '/') == -1
|
||||
for hint in s:plugin_hints
|
||||
let hint_path = path . '/' . hint
|
||||
if isdirectory(hint_path) || filereadable(hint_path)
|
||||
let name = fnamemodify(path, ':t')
|
||||
call add(s:plugins, [path . '/', name])
|
||||
call add(seen, path . '/')
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
endfor
|
||||
|
||||
call sort(s:plugins, function('s:plugin_sort'))
|
||||
call add(s:plugins, [runtime_path, '[runtime]'])
|
||||
|
||||
if vimrc_path != home
|
||||
call add(s:plugins, [vimrc_path, '[vimrc]'])
|
||||
endif
|
||||
|
||||
if vimrc_path != default_vimrc_path && isdirectory(default_vimrc_path)
|
||||
call add(s:plugins, [default_vimrc_path, '[vimrc]'])
|
||||
endif
|
||||
|
||||
if isdirectory(nvim_config)
|
||||
call add(s:plugins, [nvim_config, '[vimrc]'])
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:get_plugin(fname) abort
|
||||
for [path, name] in s:plugins
|
||||
if len(path) < len(a:fname) && a:fname[:len(path)-1] == path
|
||||
return name
|
||||
endif
|
||||
endfor
|
||||
|
||||
return '[unknown]'
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:get_samples(cmd, count, tmp) abort
|
||||
let c = 0
|
||||
let phase_order = []
|
||||
let phases = {'startup': {'_files': {}, '_time': 0}}
|
||||
let totals = {}
|
||||
let total_time = 0
|
||||
let logs = []
|
||||
|
||||
while c < a:count
|
||||
if getchar(0) == 27
|
||||
echomsg printf('Stopped after %d samples', c)
|
||||
break
|
||||
endif
|
||||
|
||||
let c += 1
|
||||
redraw
|
||||
echo printf('Sample %d/%d', c, a:count)
|
||||
call system(a:cmd)
|
||||
|
||||
if !filereadable(a:tmp)
|
||||
echohl ErrorMsg
|
||||
echo 'Profile log wasn''t created'
|
||||
echohl None
|
||||
break
|
||||
endif
|
||||
|
||||
let phase = 'startup'
|
||||
let log = readfile(a:tmp)
|
||||
call add(logs, log)
|
||||
|
||||
for line in log
|
||||
if line =~# '^\%(\d\+\.\d\+\s*\)\{2}:'
|
||||
if c == 1
|
||||
call add(phase_order, phase)
|
||||
endif
|
||||
" call add(phases, {'phase': phase, 'times': cur_phase})
|
||||
" let cur_phase = {}
|
||||
let phase = matchstr(line, '\d\+\.\d\+: \zs.*')
|
||||
if !has_key(phases, phase)
|
||||
let phases[phase] = {'_files': {}, '_time': 0}
|
||||
endif
|
||||
elseif line =~# '^\%(\d\+\.\d\+\s*\)\{3}: sourcing '
|
||||
let [time, fname] = split(matchstr(line, '\d\+\.\d\+: .*'), ':\s*sourcing\s*')
|
||||
let plugin = s:get_plugin(fname)
|
||||
|
||||
if !has_key(phases[phase], plugin)
|
||||
let phases[phase][plugin] = 0
|
||||
let phases[phase]['_files'][plugin] = {}
|
||||
endif
|
||||
|
||||
if !has_key(phases[phase]['_files'][plugin], fname)
|
||||
let phases[phase]['_files'][plugin][fname] = 0
|
||||
endif
|
||||
|
||||
if !has_key(totals, plugin)
|
||||
let totals[plugin] = 0
|
||||
endif
|
||||
|
||||
let t = str2float(time)
|
||||
let phases[phase][plugin] += t
|
||||
let phases[phase]['_time'] += t
|
||||
let phases[phase]['_files'][plugin][fname] += t
|
||||
let totals[plugin] += t
|
||||
let total_time += t
|
||||
endif
|
||||
endfor
|
||||
|
||||
call delete(a:tmp)
|
||||
endwhile
|
||||
|
||||
for phase in keys(phases)
|
||||
for plugin in keys(phases[phase])
|
||||
if plugin != '_files'
|
||||
let phases[phase][plugin] = phases[phase][plugin] / c
|
||||
else
|
||||
for fplugin in keys(phases[phase][plugin])
|
||||
for fname in keys(phases[phase][plugin][fplugin])
|
||||
let phases[phase][plugin][fplugin][fname] = phases[phase][plugin][fplugin][fname] / c
|
||||
endfor
|
||||
endfor
|
||||
endif
|
||||
endfor
|
||||
endfor
|
||||
|
||||
for plugin in keys(totals)
|
||||
let totals[plugin] = totals[plugin] / c
|
||||
endfor
|
||||
|
||||
let total_time = total_time / c
|
||||
|
||||
return [total_time, totals, phase_order, phases, logs]
|
||||
endfunction
|
||||
|
||||
|
||||
function! SpaceVim#dev#profile#run(...)
|
||||
let sample_count = 10
|
||||
let vimrc = s:get_vimrc()
|
||||
let extra_args = []
|
||||
|
||||
for arg in a:000
|
||||
if arg == '--'
|
||||
call add(extra_args, '')
|
||||
elseif !empty(extra_args)
|
||||
call add(extra_args, arg)
|
||||
elseif arg =~# '\d\+'
|
||||
let sample_count = str2nr(arg)
|
||||
elseif arg =~# '\f\+' && filereadable(expand(arg))
|
||||
let vimrc = expand(arg)
|
||||
endif
|
||||
endfor
|
||||
|
||||
if a:0 && type(a:1) == type(0) && a:1 > 0
|
||||
let sample_count = a:1
|
||||
endif
|
||||
|
||||
call s:init_plugins(vimrc)
|
||||
|
||||
if exists('v:progpath') && !empty(v:progpath) && executable(v:progpath)
|
||||
let exe = v:progpath
|
||||
else
|
||||
let exe = has('nvim') ? 'nvim' : 'vim'
|
||||
if has('win32')
|
||||
let exe .= '.exe'
|
||||
endif
|
||||
endif
|
||||
|
||||
let tmp = tempname()
|
||||
let wintmp = ''
|
||||
let quiet_arg = has('nvim') ? '--headless' : '--not-a-term'
|
||||
let args = ' -i NONE --startuptime ' . tmp . ' +qa!'
|
||||
if !empty(vimrc)
|
||||
let args = ' -u ' . vimrc . args
|
||||
endif
|
||||
|
||||
if !empty(extra_args)
|
||||
let args .= ' ' . join(extra_args, ' ')
|
||||
endif
|
||||
|
||||
call system(exe . ' ' . quiet_arg . ' +qa!')
|
||||
|
||||
if v:shell_error
|
||||
" Use `script` so Vim doesn't issue a delay warning
|
||||
if has('macunix')
|
||||
let cmd = 'script -q /dev/null ' . exe . args
|
||||
elseif has('win32')
|
||||
" Just hope for the best
|
||||
let wintmp = tempname()
|
||||
let cmd = exe . args . ' >' . wintmp .' 2>&1'
|
||||
else
|
||||
let cmd = 'script -q -c "' . exe . args . '" /dev/null'
|
||||
endif
|
||||
else
|
||||
let cmd = exe . ' ' . quiet_arg . args
|
||||
endif
|
||||
|
||||
echomsg 'Sampling with command:' cmd
|
||||
|
||||
let [total_time, totals, phase_order,
|
||||
\ phases, logs] = s:get_samples(cmd, sample_count, tmp)
|
||||
let total_samples = len(logs)
|
||||
|
||||
if !empty(wintmp) && filereadable(wintmp)
|
||||
call delete(wintmp)
|
||||
endif
|
||||
|
||||
let level_time = 1000 / (len(s:levels) - 1)
|
||||
let l = float2nr(floor(min([float2nr(total_time), 1000]) / level_time))
|
||||
let level = s:levels[l]
|
||||
|
||||
let lines = [printf('Total Time: %8.3f -- %s', total_time, level), '']
|
||||
|
||||
let slowest = sort(items(totals), function('s:result_sort'))[:9]
|
||||
let width = max(map(copy(slowest), 'len(v:val[0])'))
|
||||
let lines += ['', printf('Slowest %d plugins (out of %d)~', len(slowest), len(totals))]
|
||||
|
||||
for [plugin, time] in slowest
|
||||
call add(lines, printf("%*s\t%-8.3f", width, plugin, time))
|
||||
endfor
|
||||
|
||||
let lines += ['', 'Phase Detail:~', '']
|
||||
|
||||
for phase in phase_order
|
||||
let item = phases[phase]
|
||||
let files = remove(item, '_files')
|
||||
let phase_total = remove(item, '_time')
|
||||
|
||||
if empty(item)
|
||||
continue
|
||||
endif
|
||||
|
||||
let lines += [printf('%s (%0.3f)~', phase, phase_total)]
|
||||
for [plugin, time] in sort(items(item), function('s:result_sort'))
|
||||
let lines += [printf("%-8.3f %s >", time, plugin)]
|
||||
for [fname, time] in sort(items(files[plugin]), function('s:result_sort'))
|
||||
let lines += [printf("\t%-8.3f %s", time, fname)]
|
||||
endfor
|
||||
let lines += ['<']
|
||||
endfor
|
||||
let lines += ['']
|
||||
endfor
|
||||
|
||||
let banner_line = repeat('=', 34)
|
||||
let lines += ['', printf('%s FULL LOGS %s', banner_line, banner_line), '']
|
||||
|
||||
let i = 0
|
||||
for log in logs
|
||||
let i += 1
|
||||
let lines += [printf('Log %d/%d >', i, total_samples)]
|
||||
let l = 0
|
||||
for line in log
|
||||
if line !~# '^\s*$'
|
||||
let log = log[l :]
|
||||
break
|
||||
endif
|
||||
let l += 1
|
||||
endfor
|
||||
let lines += map(log, '" " . v:val') + ['<']
|
||||
endfor
|
||||
|
||||
enew
|
||||
silent %put=lines
|
||||
call cursor(1, 1)
|
||||
silent delete _
|
||||
set buftype=nofile syntax=help foldmethod=marker foldmarker=>,< nomodified
|
||||
silent file startup-log.txt
|
||||
normal! zM
|
||||
endfunction
|
@ -33,4 +33,4 @@ command! -nargs=1 IssueEdit call SpaceVim#dev#issuemanager#edit(<f-args>)
|
||||
command! -nargs=1 PullCreate call SpaceVim#dev#pull#create(<f-args>)
|
||||
command! -nargs=1 PullMerge call SpaceVim#dev#pull#merge(<f-args>)
|
||||
command! ReleaseSpaceVim call SpaceVim#dev#releases#open()
|
||||
|
||||
command! -nargs=* -complete=file Profile call SpaceVim#dev#profile#run(<f-args>)
|
||||
|
Loading…
Reference in New Issue
Block a user