1
0
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:
Wang Shidong 2020-05-17 21:09:14 +08:00 committed by GitHub
parent 5d6df904a3
commit 8d294a09bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 372 additions and 1 deletions

View 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

View File

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