diagnostics.zsh 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. # diagnostics.zsh
  2. #
  3. # Diagnostic and debugging support for oh-my-zsh
  4. # omz_diagnostic_dump()
  5. #
  6. # Author: Andrew Janke <andrew@apjanke.net>
  7. #
  8. # Usage:
  9. #
  10. # omz_diagnostic_dump [-v] [-V] [file]
  11. #
  12. # NOTE: This is a work in progress. Its interface and behavior are going to change,
  13. # and probably in non-back-compatible ways.
  14. #
  15. # Outputs a bunch of information about the state and configuration of
  16. # oh-my-zsh, zsh, and the user's system. This is intended to provide a
  17. # bunch of context for diagnosing your own or a third party's problems, and to
  18. # be suitable for posting to public bug reports.
  19. #
  20. # The output is human-readable and its format may change over time. It is not
  21. # suitable for parsing. All the output is in one single file so it can be posted
  22. # as a gist or bug comment on GitHub. GitHub doesn't support attaching tarballs
  23. # or other files to bugs; otherwise, this would probably have an option to produce
  24. # tarballs that contain copies of the config and customization files instead of
  25. # catting them all in to one file.
  26. #
  27. # This is intended to be widely portable, and run anywhere that oh-my-zsh does.
  28. # Feel free to report any portability issues as bugs.
  29. #
  30. # This is written in a defensive style so it still works (and can detect) cases when
  31. # basic functionality like echo and which have been redefined. In particular, almost
  32. # everything is invoked with "builtin" or "command", to work in the face of user
  33. # redefinitions.
  34. #
  35. # OPTIONS
  36. #
  37. # [file] Specifies the output file. If not given, a file in the current directory
  38. # is selected automatically.
  39. #
  40. # -v Increase the verbosity of the dump output. May be specified multiple times.
  41. # Verbosity levels:
  42. # 0 - Basic info, shell state, omz configuration, git state
  43. # 1 - (default) Adds key binding info and configuration file contents
  44. # 2 - Adds zcompdump file contents
  45. #
  46. # -V Reduce the verbosity of the dump output. May be specified multiple times.
  47. #
  48. # TODO:
  49. # * Multi-file capture
  50. # * Add automatic gist uploading
  51. # * Consider whether to move default output file location to TMPDIR. More robust
  52. # but less user friendly.
  53. #
  54. autoload -Uz is-at-least
  55. function omz_diagnostic_dump() {
  56. emulate -L zsh
  57. builtin echo "Generating diagnostic dump; please be patient..."
  58. local thisfcn=omz_diagnostic_dump
  59. local -A opts
  60. local opt_verbose opt_noverbose opt_outfile
  61. local timestamp=$(date +%Y%m%d-%H%M%S)
  62. local outfile=omz_diagdump_$timestamp.txt
  63. builtin zparseopts -A opts -D -- "v+=opt_verbose" "V+=opt_noverbose"
  64. local verbose n_verbose=${#opt_verbose} n_noverbose=${#opt_noverbose}
  65. (( verbose = 1 + n_verbose - n_noverbose ))
  66. if [[ ${#*} > 0 ]]; then
  67. opt_outfile=$1
  68. fi
  69. if [[ ${#*} > 1 ]]; then
  70. builtin echo "$thisfcn: error: too many arguments" >&2
  71. return 1
  72. fi
  73. if [[ -n "$opt_outfile" ]]; then
  74. outfile="$opt_outfile"
  75. fi
  76. # Always write directly to a file so terminal escape sequences are
  77. # captured cleanly
  78. _omz_diag_dump_one_big_text &> "$outfile"
  79. if [[ $? != 0 ]]; then
  80. builtin echo "$thisfcn: error while creating diagnostic dump; see $outfile for details"
  81. fi
  82. builtin echo
  83. builtin echo Diagnostic dump file created at: "$outfile"
  84. builtin echo
  85. builtin echo To share this with OMZ developers, post it as a gist on GitHub
  86. builtin echo at "https://gist.github.com" and share the link to the gist.
  87. builtin echo
  88. builtin echo "WARNING: This dump file contains all your zsh and omz configuration files,"
  89. builtin echo "so don't share it publicly if there's sensitive information in them."
  90. builtin echo
  91. }
  92. function _omz_diag_dump_one_big_text() {
  93. local program programs progfile md5
  94. builtin echo oh-my-zsh diagnostic dump
  95. builtin echo
  96. builtin echo $outfile
  97. builtin echo
  98. # Basic system and zsh information
  99. command date
  100. command uname -a
  101. builtin echo OSTYPE=$OSTYPE
  102. builtin echo ZSH_VERSION=$ZSH_VERSION
  103. builtin echo User: $USERNAME
  104. builtin echo umask: $(umask)
  105. builtin echo
  106. _omz_diag_dump_os_specific_version
  107. builtin echo
  108. # Installed programs
  109. programs=(sh zsh ksh bash sed cat grep ls find git posh)
  110. local progfile="" extra_str="" sha_str=""
  111. for program in $programs; do
  112. extra_str="" sha_str=""
  113. progfile=$(builtin which $program)
  114. if [[ $? == 0 ]]; then
  115. if [[ -e $progfile ]]; then
  116. if builtin whence shasum &>/dev/null; then
  117. sha_str=($(command shasum $progfile))
  118. sha_str=$sha_str[1]
  119. extra_str+=" SHA $sha_str"
  120. fi
  121. if [[ -h "$progfile" ]]; then
  122. extra_str+=" ( -> ${progfile:A} )"
  123. fi
  124. fi
  125. builtin printf '%-9s %-20s %s\n' "$program is" "$progfile" "$extra_str"
  126. else
  127. builtin echo "$program: not found"
  128. fi
  129. done
  130. builtin echo
  131. builtin echo Command Versions:
  132. builtin echo "zsh: $(zsh --version)"
  133. builtin echo "this zsh session: $ZSH_VERSION"
  134. builtin echo "bash: $(bash --version | command grep bash)"
  135. builtin echo "git: $(git --version)"
  136. builtin echo "grep: $(grep --version)"
  137. builtin echo
  138. # Core command definitions
  139. _omz_diag_dump_check_core_commands || return 1
  140. builtin echo
  141. # ZSH Process state
  142. builtin echo Process state:
  143. builtin echo pwd: $PWD
  144. if builtin whence pstree &>/dev/null; then
  145. builtin echo Process tree for this shell:
  146. pstree -p $$
  147. else
  148. ps -fT
  149. fi
  150. builtin set | command grep -a '^\(ZSH\|plugins\|TERM\|LC_\|LANG\|precmd\|chpwd\|preexec\|FPATH\|TTY\|DISPLAY\|PATH\)\|OMZ'
  151. builtin echo
  152. #TODO: Should this include `env` instead of or in addition to `export`?
  153. builtin echo Exported:
  154. builtin echo $(builtin export | command sed 's/=.*//')
  155. builtin echo
  156. builtin echo Locale:
  157. command locale
  158. builtin echo
  159. # Zsh installation and configuration
  160. builtin echo Zsh configuration:
  161. builtin echo setopt: $(builtin setopt)
  162. builtin echo
  163. builtin echo zstyle:
  164. builtin zstyle
  165. builtin echo
  166. builtin echo 'compaudit output:'
  167. compaudit
  168. builtin echo
  169. builtin echo '$fpath directories:'
  170. command ls -lad $fpath
  171. builtin echo
  172. # Oh-my-zsh installation
  173. builtin echo oh-my-zsh installation:
  174. command ls -ld ~/.z*
  175. command ls -ld ~/.oh*
  176. builtin echo
  177. builtin echo oh-my-zsh git state:
  178. (builtin cd $ZSH && builtin echo "HEAD: $(git rev-parse HEAD)" && git remote -v && git status | command grep "[^[:space:]]")
  179. if [[ $verbose -ge 1 ]]; then
  180. (builtin cd $ZSH && git reflog --date=default | command grep pull)
  181. fi
  182. builtin echo
  183. if [[ -e $ZSH_CUSTOM ]]; then
  184. local custom_dir=$ZSH_CUSTOM
  185. if [[ -h $custom_dir ]]; then
  186. custom_dir=$(builtin cd $custom_dir && pwd -P)
  187. fi
  188. builtin echo "oh-my-zsh custom dir:"
  189. builtin echo " $ZSH_CUSTOM ($custom_dir)"
  190. (builtin cd ${custom_dir:h} && command find ${custom_dir:t} -name .git -prune -o -print)
  191. builtin echo
  192. fi
  193. # Key binding and terminal info
  194. if [[ $verbose -ge 1 ]]; then
  195. builtin echo "bindkey:"
  196. builtin bindkey
  197. builtin echo
  198. builtin echo "infocmp:"
  199. command infocmp -L
  200. builtin echo
  201. fi
  202. # Configuration file info
  203. local zdotdir=${ZDOTDIR:-$HOME}
  204. builtin echo "Zsh configuration files:"
  205. local cfgfile cfgfiles
  206. # Some files for bash that zsh does not use are intentionally included
  207. # to help with diagnosing behavior differences between bash and zsh
  208. cfgfiles=( /etc/zshenv /etc/zprofile /etc/zshrc /etc/zlogin /etc/zlogout
  209. $zdotdir/.zshenv $zdotdir/.zprofile $zdotdir/.zshrc $zdotdir/.zlogin $zdotdir/.zlogout
  210. ~/.zsh.pre-oh-my-zsh
  211. /etc/bashrc /etc/profile ~/.bashrc ~/.profile ~/.bash_profile ~/.bash_logout )
  212. command ls -lad $cfgfiles 2>&1
  213. builtin echo
  214. if [[ $verbose -ge 1 ]]; then
  215. for cfgfile in $cfgfiles; do
  216. _omz_diag_dump_echo_file_w_header $cfgfile
  217. done
  218. fi
  219. builtin echo
  220. builtin echo "Zsh compdump files:"
  221. local dumpfile dumpfiles
  222. command ls -lad $zdotdir/.zcompdump*
  223. dumpfiles=( $zdotdir/.zcompdump*(N) )
  224. if [[ $verbose -ge 2 ]]; then
  225. for dumpfile in $dumpfiles; do
  226. _omz_diag_dump_echo_file_w_header $dumpfile
  227. done
  228. fi
  229. }
  230. function _omz_diag_dump_check_core_commands() {
  231. builtin echo "Core command check:"
  232. local redefined name builtins externals reserved_words
  233. redefined=()
  234. # All the zsh non-module builtin commands
  235. # These are taken from the zsh reference manual for 5.0.2
  236. # Commands from modules should not be included.
  237. # (For back-compatibility, if any of these are newish, they should be removed,
  238. # or at least made conditional on the version of the current running zsh.)
  239. # "history" is also excluded because OMZ is known to redefine that
  240. reserved_words=( do done esac then elif else fi for case if while function
  241. repeat time until select coproc nocorrect foreach end '!' '[[' '{' '}'
  242. )
  243. builtins=( alias autoload bg bindkey break builtin bye cd chdir command
  244. comparguments compcall compctl compdescribe compfiles compgroups compquote comptags
  245. comptry compvalues continue dirs disable disown echo echotc echoti emulate
  246. enable eval exec exit false fc fg functions getln getopts hash
  247. jobs kill let limit log logout noglob popd print printf
  248. pushd pushln pwd r read rehash return sched set setopt shift
  249. source suspend test times trap true ttyctl type ulimit umask unalias
  250. unfunction unhash unlimit unset unsetopt vared wait whence where which zcompile
  251. zle zmodload zparseopts zregexparse zstyle )
  252. if is-at-least 5.1; then
  253. reserved_word+=( declare export integer float local readonly typeset )
  254. else
  255. builtins+=( declare export integer float local readonly typeset )
  256. fi
  257. builtins_fatal=( builtin command local )
  258. externals=( zsh )
  259. for name in $reserved_words; do
  260. if [[ $(builtin whence -w $name) != "$name: reserved" ]]; then
  261. builtin echo "reserved word '$name' has been redefined"
  262. builtin which $name
  263. redefined+=$name
  264. fi
  265. done
  266. for name in $builtins; do
  267. if [[ $(builtin whence -w $name) != "$name: builtin" ]]; then
  268. builtin echo "builtin '$name' has been redefined"
  269. builtin which $name
  270. redefined+=$name
  271. fi
  272. done
  273. for name in $externals; do
  274. if [[ $(builtin whence -w $name) != "$name: command" ]]; then
  275. builtin echo "command '$name' has been redefined"
  276. builtin which $name
  277. redefined+=$name
  278. fi
  279. done
  280. if [[ -n "$redefined" ]]; then
  281. builtin echo "SOME CORE COMMANDS HAVE BEEN REDEFINED: $redefined"
  282. else
  283. builtin echo "All core commands are defined normally"
  284. fi
  285. }
  286. function _omz_diag_dump_echo_file_w_header() {
  287. local file=$1
  288. if [[ ( -f $file || -h $file ) ]]; then
  289. builtin echo "========== $file =========="
  290. if [[ -h $file ]]; then
  291. builtin echo "========== ( => ${file:A} ) =========="
  292. fi
  293. command cat $file
  294. builtin echo "========== end $file =========="
  295. builtin echo
  296. elif [[ -d $file ]]; then
  297. builtin echo "File '$file' is a directory"
  298. elif [[ ! -e $file ]]; then
  299. builtin echo "File '$file' does not exist"
  300. else
  301. command ls -lad "$file"
  302. fi
  303. }
  304. function _omz_diag_dump_os_specific_version() {
  305. local osname osver version_file version_files
  306. case "$OSTYPE" in
  307. darwin*)
  308. osname=$(command sw_vers -productName)
  309. osver=$(command sw_vers -productVersion)
  310. builtin echo "OS Version: $osname $osver build $(sw_vers -buildVersion)"
  311. ;;
  312. cygwin)
  313. command systeminfo | command head -n 4 | command tail -n 2
  314. ;;
  315. esac
  316. if builtin which lsb_release >/dev/null; then
  317. builtin echo "OS Release: $(command lsb_release -s -d)"
  318. fi
  319. version_files=( /etc/*-release(N) /etc/*-version(N) /etc/*_version(N) )
  320. for version_file in $version_files; do
  321. builtin echo "$version_file:"
  322. command cat "$version_file"
  323. builtin echo
  324. done
  325. }