Parent directory

diagnostics.zsh

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