# 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=\$((\${frame_${p}_time} + \${value}))" } frame_dec_time() { local p="${1}" local value="${2}" eval "frame_${p}_time=\$((\${frame_${p}_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}=\$((\${fn_timing_${name}} + \ \${time}))" eval "fn_calls_${name}=\$((\${fn_calls_${name}} + 1))" ;; *) eval "fn_timing_${name}=\${time}" eval "fn_calls_${name}=1" fns="${fns} ${name}" ;; esac total_runtime=$((${total_runtime} + ${time})) } 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 # Convert to int. 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= local time_per_call= printf ' self self\n' printf '%% time seconds calls ms/call name\n' for fn in ${fns}; do eval "time=\${fn_timing_${fn}}" eval "calls=\${fn_calls_${fn}}" percent=$(printf 'scale = 2; 100 * %d / %d\n' \ ${time} ${total_runtime} | bc) time=$(printf 'scale = 9; %d / 1000000000\n' ${time} | bc) time_per_call=$(printf 'scale = 6; %s * 1000 / %s\n' \ ${time} ${calls} | bc) printf '%6.2f ' ${percent} printf '%14.9f ' ${time} printf '%8d ' ${calls} printf '%11.6f ' ${time_per_call} printf '%s\n' "${fn}" done | sort -nr -k 1,4 } flat_profile() { local profile="${1}" shift 1 sp=0 fn_name='' fn_time=0 fns='' total_runtime=0 read_profile "${profile}" show_table }