# Flat profiler # # Copyright (C) 2016 Patrick "P. J." McDermott # # This file is part of the Eggshell Profiler. # # The Eggshell Profiler is free software: you can redistribute it # and/or modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, either version 3 of # the License, or (at your option) any later version. # # The Eggshell Profiler is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with the Eggshell Profiler. If not, see # . sp= fn_name= fn_start_time= fn_end_time= fns= total_runtime= sp_inc() { sp=$((${sp} + 1)) } sp_dec() { sp=$((${sp} - 1)) } frame_set() { local p="${1}" shift 1 eval "frame_${p}_name=\${fn_name}" eval "frame_${p}_time=0" } frame_get() { local p="${1}" shift 1 eval "fn_name=\${frame_${p}_name}" eval "fn_time=\${frame_${p}_time}" } frame_unset() { local p="${1}" shift 1 unset "frame_${p}_name" unset "frame_${p}_time" } frame_inc_time() { local p="${1}" local value="${2}" eval "frame_${p}_time=\$(awk \ -v time=\${frame_${p}_time} -v value=\${value} \ 'BEGIN { print(time + value); }')" } frame_dec_time() { local p="${1}" local value="${2}" eval "frame_${p}_time=\$(awk \ -v time=\${frame_${p}_time} -v value=\${value} \ 'BEGIN { print(time - value); }')" } ctxsw() { local prev_sp="${1}" local next_sp="${2}" local time="${3}" shift 2 frame_inc_time ${prev_sp} ${time} frame_dec_time ${next_sp} ${time} } record() { local name="${1}" local time="${2}" shift 2 case " ${fns} " in "${name}") eval "fn_timing_${name}=\$(awk \ -v time=\${fn_timing_${name}} -v inc=\${time} \ 'BEGIN { print(time + inc); }')" eval "fn_calls_${name}=\$((\${fn_calls_${name}} + 1))" ;; *) eval "fn_timing_${name}=\${time}" eval "fn_calls_${name}=1" fns="${fns} ${name}" ;; esac total_runtime=$(awk -v total=${total_runtime} -v inc=${time} \ 'BEGIN { print(total + inc); }') } fn_begin() { local name="${1}" local time="${2}" shift 2 local old_sp= old_sp=${sp} sp_inc fn_name="${name}" frame_set ${sp} ctxsw ${old_sp} ${sp} ${time} } fn_end() { local name="${1}" local time="${2}" shift 2 local old_sp= old_sp=${sp} sp_dec ctxsw ${old_sp} ${sp} ${time} frame_get ${old_sp} record ${fn_name} ${fn_time} frame_unset ${old_sp} } read_profile() { local profile="${1}" shift 1 while read time change fn; do #time="${time%.*}${time#*.}" case "${change}" in 'B') fn_begin "${fn}" "${time}" ;; 'E') fn_end "${fn}" "${time}" ;; esac done <"${profile}" } show_table() { local fn= local time= local percent= local calls= printf ' self\n' printf '%% time seconds calls name\n' for fn in ${fns}; do eval "time=\${fn_timing_${fn}}" percent=$(awk -v time=${time} -v total=${total_runtime} \ 'BEGIN { print(time / total * 100); }') eval "calls=\${fn_calls_${fn}}" printf '%6.2f ' ${percent} printf '%14.9f ' ${time} printf '%8d ' ${calls} printf '%s\n' "${fn}" done | sort -r -k 2,4 } flat_profile() { local profile="${1}" shift 1 sp=0 fn_name='' fn_time=0 fns='' total_runtime=0 read_profile "${profile}" show_table }