summaryrefslogtreecommitdiffstats
path: root/eshprof/flat-profile.esh
diff options
context:
space:
mode:
Diffstat (limited to 'eshprof/flat-profile.esh')
-rw-r--r--eshprof/flat-profile.esh199
1 files changed, 199 insertions, 0 deletions
diff --git a/eshprof/flat-profile.esh b/eshprof/flat-profile.esh
new file mode 100644
index 0000000..ecf2a6e
--- /dev/null
+++ b/eshprof/flat-profile.esh
@@ -0,0 +1,199 @@
+# 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
+# <http://www.gnu.org/licenses/>.
+
+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
+}