make_small() { local w=$1 echo $w | tr A-Z a-z } show_instances() { local cur="$1" local bufs=$(trace-cmd list -B) if [ "$bufs" == "No buffer instances defined" ]; then return 0 fi COMPREPLY=( $(compgen -W "${bufs}" -- "${cur}") ) return 0 } show_virt() { local cur="$1" if ! which virsh &>/dev/null; then return 1 fi local virt=`virsh list | awk '/^ *[0-9]/ { print $2 }'` COMPREPLY=( $(compgen -W "${virt}" -- "${cur}") ) return 0 } show_options() { local cur="$1" local options=$(trace-cmd list -o | sed -e 's/^\(no\)*\(.*\)/\2 no\2/') COMPREPLY=( $(compgen -W "${options}" -- "${cur}") ) return 0 } __show_files() { COMPREPLY=( $(compgen -f -- "$cur") ) if [ ${#COMPREPLY[@]} -gt 1 ]; then return 0; fi # directories get '/' instead of space DIRS=( $(compgen -d -- "$cur")) if [ ${#DIRS[@]} -eq 1 ]; then compopt -o nospace COMPREPLY="$DIRS/" return 0; fi return 0 } cmd_options() { local type="$1" local cur="$2" local extra="$3" local cmds=$(trace-cmd $type -h 2>/dev/null|grep "^ *-" | \ sed -e 's/ *\(-[^ ]*\).*/\1/') COMPREPLY=( $(compgen -W "${cmds} ${extra}" -- "${cur}") ) } cmd_options_files() { cmd_options "$1" "$2" "$3" if [ ${#COMPREPLY[@]} -eq 0 ]; then __show_files "${cur}" fi } plugin_options() { local cur="$1" local opts=$(trace-cmd list -O | sed -ne 's/option://p') COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) } compression_param() { local opts=$(trace-cmd list -c | grep -v 'Supported' | cut -d "," -f1) opts+=" any none " COMPREPLY=( $(compgen -W "${opts}") ) } list_events() { local cur=$1 local list=$(trace-cmd list -e "$cur") local prefix=${cur%%:*} if [ -z "$cur" -o "$cur" != "$prefix" ]; then echo "${list}" else local events=$(for e in $list; do echo ${e/*:/}; done | sort -u) local systems=$(for s in $list; do echo ${s/:*/:}; done | sort -u) echo "${events} ${systems}" fi } __trace_cmd_list_complete() { local prev=$1 local cur=$2 shift 2 local words=("$@") case "$prev" in list) local cmds=$(trace-cmd list -h |egrep "^ {10}-" | \ sed -e 's/.*\(-.\).*/\1/') COMPREPLY=( $(compgen -W "${cmds}" -- "${cur}") ) ;; -e) local list=`list_events "$cur"` COMPREPLY=( $(compgen -W "all $list" -- "${cur}") ) ;; *) size=${#words[@]} if [ $size -gt 3 ]; then if [ "$cur" == "-" ]; then let size=$size-3 else let size=$size-2 fi local w="${words[$size]}" if [ "$w" == "-e" ]; then local cmds=$(trace-cmd list -h |egrep "^ {12}-" | \ sed -e 's/.*\(-.\).*/\1/') COMPREPLY=( $(compgen -W "${cmds}" -- "${cur}") ) fi fi ;; esac } __trace_cmd_show_complete() { local prev=$1 local cur=$2 shift 2 local words=("$@") case "$prev" in -B) show_instances "$cur" ;; --hist|--trigger) local list=`list_events $cur` COMPREPLY=( $(compgen -W "all ${list}" -- "${cur}") ) ;; *) cmd_options show "$cur" ;; esac } __trace_cmd_extract_complete() { local prev=$1 local cur=$2 shift 2 local words=("$@") case "$prev" in extract) cmd_options "$prev" "$cur" ;; -B) show_instances "$cur" ;; *) __show_files ;; esac } __trace_cmd_record_complete() { local prev=$1 local cur=$2 shift 2 local words=("$@") case "$prev" in -e) local list=`list_events $cur` COMPREPLY=( $(compgen -W "all ${list}" -- "${cur}") ) # This is still to handle the "*:*" special case if [[ -n "$prefix" ]]; then local reply_n=${#COMPREPLY[*]} for (( i = 0; i < $reply_n; i++)); do COMPREPLY[$i]=${COMPREPLY[i]##${prefix}:} done fi ;; -p) local plugins=$(trace-cmd list -p) COMPREPLY=( $(compgen -W "${plugins}" -- "${cur}" ) ) ;; -l|-n|-g) # This is extremely slow still (may take >1sec). local funcs=$(trace-cmd list -f | sed 's/ .*//') COMPREPLY=( $(compgen -W "${funcs}" -- "${cur}") ) ;; -B) show_instances "$cur" ;; -O) show_options "$cur" ;; -A) if ! show_virt "$cur"; then cmd_options_files record "$cur" fi ;; --compression) compression_param ;; *) # stream start and profile do not show all options cmd_options_files record "$cur" ;; esac } __trace_cmd_report_complete() { local prev=$1 local cur=$2 shift 2 local words=("$@") case "$prev" in -O) plugin_options "$cur" ;; *) cmd_options_files report "$cur" ;; esac } dynevent_options() { local cur="$1" local opts=("kprobe" "kretprobe" "uprobe" "uretprobe" "eprobe" "synth" "all") COMPREPLY=( $(compgen -W "${opts[*]}" -- "${cur}") ) } __trace_cmd_reset_complete() { local prev=$1 local cur=$2 shift 2 local words=("$@") case "$prev" in -B) show_instances "$cur" ;; -k) dynevent_options "$cur" ;; *) cmd_options reset "$cur" ;; esac } __trace_cmd_dump_complete() { local prev=$1 local cur=$2 shift 2 local words=("$@") case "$prev" in -i) __show_files ;; *) cmd_options dump "$cur" ;; esac } __trace_cmd_convert_complete() { local prev=$1 local cur=$2 shift 2 local words=("$@") case "$prev" in -i) __show_files ;; -o) __show_files ;; --compression) compression_param ;; *) cmd_options convert "$cur" ;; esac } ##### SQLHIST COMMANDS ##### prev_keyword() { local i=$1 shift local words=("$@") while [ $i -gt 0 ]; do let i=$i-1 local w=`make_small ${words[$i]}` case $w in select) echo "select" return ;; from) echo "from" return ;; as) echo "as" return ;; on) echo "on" return ;; join) echo "join" return ;; where) echo "where" return ;; *) if [ "$w" != "${w%%,}" ]; then echo "," return fi if [ "$w" != "${w%%=}" ]; then echo "=" return fi ;; esac done echo "" } prev_command() { local i=$1 shift local words=("$@") while [ $i -gt 0 ]; do let i=$i-1 local w=`make_small ${words[$i]}` case $w in select) echo "select" return ;; from) echo "from" return ;; on) echo "on" return ;; join) echo "join" return ;; where) echo "where" return ;; esac done echo "" } add_vars() { local words=("$@") local i=$COMP_CWORD let found_from=0 while [ $i -gt 0 ]; do let i=$i-1 local w=`make_small ${words[$i]}` case $w in "from") let found_from=1 ;; *) if [ $found_from ]; then start=`echo $w | sed -e 's/\.[^\.]*$//'` if [ "$start" != "$w" -a "$start" == "${start%%\.*}" ]; then echo -n "$start " fi fi ;; esac done } add_options() { local cur="$1" local list="$2" COMPREPLY=( $(compgen -W "${list}" -- "${cur}") ) } print_fields() { local event=$1 local var=$2 local extra=$3 local list=`trace-cmd list -e "^${event/\./:}\$" -F | cut -d';' -f1 | sed -ne 's/\t.*:.* \(.*\)/\1/p' |sed -e 's/\[.*\]//'` for field in $list $extra; do echo "$event.$field" if [ ! -z "$var" ]; then echo "$var.$field" fi done } select_options() { local cur=$1 local extra=$2 local list=`list_events "${cur/\./:}" | sed -e 's/:/./g'` local select_list=" TIMESTAMP_DELTA TIMESTAMP_DELTA_USECS $extra" local select_fields=" TIMESTAMP TIMESTAMP_USECS STACKTRACE" add_options "$cur" "$list $select_list" local cnt=${#COMPREPLY[@]} if [ $cnt -eq 1 ]; then local comp=${COMPREPLY[0]} local w=$(compgen -W "$select_list" -- "$comp" ) if [ -z "$w" ]; then COMPREPLY=("$comp.") compopt -o nospace fi elif [ $cnt -eq 0 ]; then local w=`echo $cur | sed -e 's/\.[^\.]*$//'` list=`print_fields $w "" "$select_fields"` COMPREPLY=( $(compgen -W "${list}" -- "${cur}") ) fi } check_as() { local words=("$@") last_key=`prev_keyword $COMP_CWORD ${words[@]}` if [ "$last_key" != "as" ]; then echo -n "AS" fi } on_list() { local type=$1 shift local words=("$@") local i=$COMP_CWORD local var="" while [ $i -gt 0 ]; do let i=$i-1 local w=`make_small ${words[$i]}` case $w in "from"|"join") if [ $w == $type ]; then print_fields ${words[$i+1]} "$var" return fi var="" ;; as) var=${words[$i+1]} ;; esac done } update_completion() { local cur=$1 shift local words=("$@") if [ ${#COMPREPLY[@]} -gt 0 ]; then return fi for w in ${words[@]}; do if [ "$w" != "${w##$cur}" ]; then COMPREPLY=("$w") return fi done } __trace_cmd_sqlhist_complete() { local prev=$1 local cur=$2 shift 2 local words=("$@") if [ "$cur" != "${cur%%,}" ]; then COMPREPLY=("$cur") return fi local p=`make_small $prev` if [ "$p" != "${p%%,}" ]; then p=`prev_command $COMP_CWORD ${words[@]}` fi case "$p" in "sqlhist") cmd_options sqlhist "$cur" "SELECT" update_completion "$cur" select ;; "select") select_options "$cur" ;; "on") list=`on_list "from" ${words[@]}` add_options "$cur" "$list" ;; "as") local last_cmd=`prev_command $COMP_CWORD ${words[@]}` case $last_cmd in "from"|"join") list=`add_vars ${words[@]}` if [ ! -z "$list" ]; then add_options "$cur" "$list" fi ;; esac ;; "from"|"join") local list=$(trace-cmd list -e "${cur/\./:}" | tr : .) local prefix=${cur/\./} if [ -z "$cur" -o "$cur" != "$prefix" ]; then COMPREPLY=( $(compgen -W "${list}" -- "${cur}") ) else local events=$(for e in $list; do echo ${e/*\./}; done | sort -u) local systems=$(for s in $list; do echo ${s/\.*/.}; done | sort -u) COMPREPLY=( $(compgen -W "all ${events} ${systems}" -- "${cur}") ) fi ;; *) local last_cmd=`prev_command $COMP_CWORD ${words[@]}` local list=`check_as ${words[@]}` local alist="" if [ ! -z "$list" ]; then alist="as" fi case $last_cmd in "select") if [ "$cur" != "${cur%%,}" ]; then select_options "$cur" "$list" else add_options "$cur" "FROM , $list" update_completion "$cur" from $alist fi ;; "from") add_options "$cur" "JOIN $list" update_completion "$cur" join $alist ;; "join") add_options "$cur" "ON $list" update_completion "$cur" on $alist ;; "on") if [ "$cur" != "${cur%%=}" ]; then COMPREPLY=("") else last_key=`prev_keyword $COMP_CWORD ${words[@]}` if [ "$last_key" == "=" ]; then if [ $prev == "=" ]; then list=`on_list "join" ${words[@]}` add_options "$cur" "$list" else add_options "$cur" "WHERE" update_completion "$cur" where fi else add_options "$cur" "=" fi fi ;; "where") ;; *) cmd_options sqlhist "$cur" "SELECT" update_completion "$cur" select ;; esac ;; esac } ##### SQLHIST COMMANDS END ##### __show_command_options() { local command="$1" local prev="$2" local cur="$3" local cmds=( $(trace-cmd --help 2>/dev/null | \ grep " - " | sed 's/^ *//; s/ -.*//') ) for cmd in ${cmds[@]}; do if [ $cmd == "$command" ]; then local opts=$(trace-cmd $cmd -h 2>/dev/null|grep "^ *-" | \ sed -e 's/ *\(-[^ ]*\).*/\1/') if [ "$prev" == "-B" ]; then for opt in ${opts[@]}; do if [ "$opt" == "-B" ]; then show_instances "$cur" return 0 fi done fi COMPREPLY=( $(compgen -W "${opts}" -- "$cur")) break fi done if [ ${#COMPREPLY[@]} -eq 0 ]; then __show_files "${cur}" fi } _trace_cmd_complete() { local cur="" local prev="" local words=() # Not to use COMP_WORDS to avoid buggy behavior of Bash when # handling with words including ":", like: # # prev="${COMP_WORDS[COMP_CWORD-1]}" # cur="${COMP_WORDS[COMP_CWORD]}" # # Instead, we use _get_comp_words_by_ref() magic. _get_comp_words_by_ref -n : cur prev words if [ "$prev" == "trace-cmd" ]; then local cmds=$(trace-cmd --help 2>/dev/null | \ grep " - " | sed 's/^ *//; s/ -.*//') COMPREPLY=( $(compgen -W "${cmds}" -- "${cur}") ) return; fi local w="${words[1]}" case "$w" in list) __trace_cmd_list_complete "${prev}" "${cur}" ${words[@]} return 0 ;; show) __trace_cmd_show_complete "${prev}" "${cur}" ${words[@]} return 0 ;; extract) __trace_cmd_extract_complete "${prev}" "${cur}" ${words[@]} return 0 ;; record|stream|start|set|profile) __trace_cmd_record_complete "${prev}" "${cur}" ${words[@]} return 0 ;; report) __trace_cmd_report_complete "${prev}" "${cur}" ${words[@]} return 0 ;; reset) __trace_cmd_reset_complete "${prev}" "${cur}" "${words[@]}" return 0 ;; dump) __trace_cmd_dump_complete "${prev}" "${cur}" ${words[@]} return 0 ;; convert) __trace_cmd_convert_complete "${prev}" "${cur}" ${words[@]} return 0 ;; sqlhist) __trace_cmd_sqlhist_complete "${prev}" "${cur}" ${words[@]} return 0 ;; *) __show_command_options "$w" "${prev}" "${cur}" ;; esac } complete -F _trace_cmd_complete trace-cmd