# perf bash and zsh completion # SPDX-License-Identifier: GPL-2.0 # Taken from git.git's completion script. __my_reassemble_comp_words_by_ref() { local exclude i j first # Which word separators to exclude? exclude="${1//[^$COMP_WORDBREAKS]}" cword_=$COMP_CWORD if [ -z "$exclude" ]; then words_=("${COMP_WORDS[@]}") return fi # List of word completion separators has shrunk; # re-assemble words to complete. for ((i=0, j=0; i < ${#COMP_WORDS[@]}; i++, j++)); do # Append each nonempty word consisting of just # word separator characters to the current word. first=t while [ $i -gt 0 ] && [ -n "${COMP_WORDS[$i]}" ] && # word consists of excluded word separators [ "${COMP_WORDS[$i]//[^$exclude]}" = "${COMP_WORDS[$i]}" ] do # Attach to the previous token, # unless the previous token is the command name. if [ $j -ge 2 ] && [ -n "$first" ]; then ((j--)) fi first= words_[$j]=${words_[j]}${COMP_WORDS[i]} if [ $i = $COMP_CWORD ]; then cword_=$j fi if (($i < ${#COMP_WORDS[@]} - 1)); then ((i++)) else # Done. return fi done words_[$j]=${words_[j]}${COMP_WORDS[i]} if [ $i = $COMP_CWORD ]; then cword_=$j fi done } # Define preload_get_comp_words_by_ref="false", if the function # __perf_get_comp_words_by_ref() is required instead. preload_get_comp_words_by_ref="true" if [ $preload_get_comp_words_by_ref = "true" ]; then type _get_comp_words_by_ref &>/dev/null || preload_get_comp_words_by_ref="false" fi [ $preload_get_comp_words_by_ref = "true" ] || __perf_get_comp_words_by_ref() { local exclude cur_ words_ cword_ if [ "$1" = "-n" ]; then exclude=$2 shift 2 fi __my_reassemble_comp_words_by_ref "$exclude" cur_=${words_[cword_]} while [ $# -gt 0 ]; do case "$1" in cur) cur=$cur_ ;; prev) prev=${words_[$cword_-1]} ;; words) words=("${words_[@]}") ;; cword) cword=$cword_ ;; esac shift done } # Define preload__ltrim_colon_completions="false", if the function # __perf__ltrim_colon_completions() is required instead. preload__ltrim_colon_completions="true" if [ $preload__ltrim_colon_completions = "true" ]; then type __ltrim_colon_completions &>/dev/null || preload__ltrim_colon_completions="false" fi [ $preload__ltrim_colon_completions = "true" ] || __perf__ltrim_colon_completions() { if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then # Remove colon-word prefix from COMPREPLY items local colon_word=${1%"${1##*:}"} local i=${#COMPREPLY[*]} while [[ $((--i)) -ge 0 ]]; do COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"} done fi } __perfcomp () { COMPREPLY=( $( compgen -W "$1" -- "$2" ) ) } __perfcomp_colon () { __perfcomp "$1" "$2" if [ $preload__ltrim_colon_completions = "true" ]; then __ltrim_colon_completions $cur else __perf__ltrim_colon_completions $cur fi } __perf_prev_skip_opts () { local i cmd_ cmds_ let i=cword-1 cmds_=$($cmd $1 --list-cmds) prev_skip_opts=() while [ $i -ge 0 ]; do if [[ ${words[i]} == $1 ]]; then return fi for cmd_ in $cmds_; do if [[ ${words[i]} == $cmd_ ]]; then prev_skip_opts=${words[i]} return fi done ((i--)) done } __perf_main () { local cmd cmd=${words[0]} COMPREPLY=() # Skip options backward and find the last perf command __perf_prev_skip_opts # List perf subcommands or long options if [ -z $prev_skip_opts ]; then if [[ $cur == --* ]]; then cmds=$($cmd --list-opts) else cmds=$($cmd --list-cmds) fi __perfcomp "$cmds" "$cur" # List possible events for -e option elif [[ $prev == @("-e"|"--event") && $prev_skip_opts == @(record|stat|top) ]]; then local cur1=${COMP_WORDS[COMP_CWORD]} local raw_evts=$($cmd list --raw-dump) local arr s tmp result if [[ "$cur1" == */* && ${cur1#*/} =~ ^[A-Z] ]]; then OLD_IFS="$IFS" IFS=" " arr=($raw_evts) IFS="$OLD_IFS" for s in ${arr[@]} do if [[ "$s" == *cpu/* ]]; then tmp=${s#*cpu/} result=$result" ""cpu/"${tmp^^} else result=$result" "$s fi done evts=${result}+$(ls /sys/bus/event_source/devices/cpu/events) else evts=${raw_evts}+$(ls /sys/bus/event_source/devices/cpu/events) fi __perfcomp_colon "$evts" "$cur1" else # List subcommands for perf commands if [[ $prev_skip_opts == @(kvm|kmem|mem|lock|sched| |data|help|script|test|timechart|trace) ]]; then subcmds=$($cmd $prev_skip_opts --list-cmds) __perfcomp_colon "$subcmds" "$cur" fi # List long option names if [[ $cur == --* ]]; then subcmd=$prev_skip_opts __perf_prev_skip_opts $subcmd subcmd=$subcmd" "$prev_skip_opts opts=$($cmd $subcmd --list-opts) __perfcomp "$opts" "$cur" fi fi } if [[ -n ${ZSH_VERSION-} ]]; then autoload -U +X compinit && compinit __perfcomp () { emulate -L zsh local c IFS=$' \t\n' local -a array for c in ${=1}; do case $c in --*=*|*.) ;; *) c="$c " ;; esac array[${#array[@]}+1]="$c" done compset -P '*[=:]' compadd -Q -S '' -a -- array && _ret=0 } __perfcomp_colon () { emulate -L zsh local cur_="${2-$cur}" local c IFS=$' \t\n' local -a array if [[ "$cur_" == *:* ]]; then local colon_word=${cur_%"${cur_##*:}"} fi for c in ${=1}; do case $c in --*=*|*.) ;; *) c="$c " ;; esac array[$#array+1]=${c#"$colon_word"} done compset -P '*[=:]' compadd -Q -S '' -a -- array && _ret=0 } _perf () { local _ret=1 cur cword prev cur=${words[CURRENT]} prev=${words[CURRENT-1]} let cword=CURRENT-1 emulate ksh -c __perf_main let _ret && _default && _ret=0 return _ret } compdef _perf perf return fi type perf &>/dev/null && _perf() { if [[ "$COMP_WORDBREAKS" != *,* ]]; then COMP_WORDBREAKS="${COMP_WORDBREAKS}," export COMP_WORDBREAKS fi local cur words cword prev if [ $preload_get_comp_words_by_ref = "true" ]; then _get_comp_words_by_ref -n =:, cur words cword prev else __perf_get_comp_words_by_ref -n =:, cur words cword prev fi __perf_main } && complete -o bashdefault -o default -o nospace -F _perf perf 2>/dev/null \ || complete -o default -o nospace -F _perf perf