Skip to content

Commit 89b6273

Browse files
committed
Enable profiling framework in core ZFSBootMenu tools
Create /lib/profiling-lib.sh, which can be sourced by any Bash script. Once sourced, a debug trap is installed that dumps the stack (but not the command being executed) to a serial port, which collects the data on the host side into a log file. After the VM exits, the performance data is converted into a format expected by flamegraph.pl (https://github.com/brendangregg/FlameGraph) and then both a flamegraph and a flamechart are created. If profiling isn't enabled, a no-op /lib/profiling-lib.sh is created. The profiling library must always exist, and must always be sourceable. Optimizations/performance changes include: - setting the loglevel as from the KCL as early as possible, to preserve all log messages > zwarn - validating that the loglevel is an integer, otherwise defaulting to 4 - validating that zbm.timeout/timeout is an integer, otherwise defaulting to 10 - adding re-import guards to libraries - removing the zlog() function and doing the logging work in the zerror/zwarn/... function instead. This means that everything but zdebug can be as lean as possible. - gating a couple serial zdebug calls behind a single zdebug check - added a fast-fail to timed_prompt to return 0 if delay isn't greater than 0. Any default values for delay/prompt are assigned after this test. - add a short-circuit to get_zbm_arg that uses bashre to check if the kcl option string is present at all in the KCL. If it's not, the next option passed to get_zbm_arg is evaluated. - Avoid pre-computing a return value until it's actually needed - cut i18n out of testing builds unless a graphical interface is requested - cut crypt-ssh and network-legacy out of testing builds unless SSH access is requested - cut out 'nvdimm fs-lib rootfs-block dm dmraid lunmask' from testing builds. These can likely be cut from release builds as well. Closes #245
1 parent fce302c commit 89b6273

16 files changed

+282
-93
lines changed

90zfsbootmenu/bin/zfs-chroot

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# vim: softtabstop=2 shiftwidth=2 expandtab
33

44
# shellcheck disable=SC1091
5+
source /lib/profiling-lib.sh >/dev/null 2>&1
6+
source /etc/zfsbootmenu.conf 2>&1 || exit 1
57
source /lib/kmsg-log-lib.sh >/dev/null 2>&1 || exit 1
68
source /lib/zfsbootmenu-core.sh >/dev/null 2>&1 || exit 1
79

@@ -12,7 +14,7 @@ zdebug "started with ${selected}"
1214
[ -n "${selected}" ] || exit 0
1315

1416
if mountpoint="$( allow_rw=yes mount_zfs "${selected}" )"; then
15-
zdebug "Mounted ${selected} to ${mountpoint}"
17+
zdebug "mounted ${selected} to ${mountpoint}"
1618
mount -t proc proc "${mountpoint}/proc"
1719
mount -t sysfs sys "${mountpoint}/sys"
1820
mount -B /dev "${mountpoint}/dev"

90zfsbootmenu/bin/zfsbootmenu

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
# Source functional libraries, logging and configuration
55
sources=(
6+
/lib/profiling-lib.sh
67
/etc/zfsbootmenu.conf
78
/lib/zfsbootmenu-core.sh
89
/lib/zfsbootmenu-lib.sh

90zfsbootmenu/bin/zkexec

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#!/bin/bash
22

33
# shellcheck disable=SC1091
4+
source /lib/profiling-lib.sh >/dev/null 2>&1
5+
source /etc/zfsbootmenu.conf 2>&1 || exit 1
46
source /lib/kmsg-log-lib.sh >/dev/null 2>&1 || exit 1
57
source /lib/zfsbootmenu-core.sh >/dev/null 2>&1 || exit 1
68

90zfsbootmenu/hook/zfsbootmenu-parse-commandline.sh

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
# Critical functions
55
sources=(
6+
/lib/profiling-lib.sh
7+
/etc/zfsbootmenu.conf
68
/lib/kmsg-log-lib.sh
79
/lib/zfsbootmenu-kcl.sh
810
)
@@ -17,9 +19,10 @@ done
1719

1820
unset src sources
1921

20-
# Source options detected at build time
21-
# shellcheck disable=SC1091
22-
[ -r /etc/zfsbootmenu.conf ] && source /etc/zfsbootmenu.conf
22+
if [ -z "${BYTE_ORDER}" ]; then
23+
zwarn "unable to determine platform endianness; assuming little-endian"
24+
BYTE_ORDER="le"
25+
fi
2326

2427
# shellcheck disable=SC2154
2528
if [ -n "${embedded_kcl}" ]; then
@@ -30,9 +33,20 @@ fi
3033
# Make sure a base directory exists
3134
mkdir -p "${BASE:=/zfsbootmenu}"
3235

33-
if [ -z "${BYTE_ORDER}" ]; then
34-
zwarn "unable to determine platform endianness; assuming little-endian"
35-
BYTE_ORDER="le"
36+
# Only zwarn/zerror will log prior to loglevel being updated by a KCL argument
37+
min_logging=4
38+
loglevel=$( get_zbm_arg loglevel )
39+
if [ -n "${loglevel}" ]; then
40+
# minimum log level of 4, so we never lose error or warning messages
41+
if [ "${loglevel}" -ge ${min_logging} ] >/dev/null 2>&1; then
42+
# shellcheck disable=1091
43+
FORCE_RELOAD=1 source /lib/kmsg-log-lib.sh >/dev/null 2>&1
44+
zinfo "setting log level from command line: ${loglevel}"
45+
else
46+
loglevel=${min_logging}
47+
fi
48+
else
49+
loglevel=${min_logging}
3650
fi
3751

3852
# Let the command line override our host id.
@@ -64,24 +78,14 @@ fi
6478
# Use the last defined console= to control menu output
6579
control_term=$( get_zbm_arg console )
6680
if [ -n "${control_term}" ]; then
81+
#shellcheck disable=SC2034
6782
control_term="/dev/${control_term%,*}"
6883
zinfo "setting controlling terminal to: ${control_term}"
6984
else
7085
control_term="/dev/tty1"
7186
zinfo "defaulting controlling terminal to: ${control_term}"
7287
fi
7388

74-
# Use loglevel to determine logging to /dev/kmsg
75-
min_logging=4
76-
loglevel=$( get_zbm_arg loglevel )
77-
if [ -n "${loglevel}" ]; then
78-
# minimum log level of 4, so we never lose error or warning messages
79-
[ "${loglevel}" -ge ${min_logging} ] || loglevel=${min_logging}
80-
zinfo "setting log level from command line: ${loglevel}"
81-
else
82-
loglevel=${min_logging}
83-
fi
84-
8589
# hostid - discover the hostid used to import a pool on failure, assume it
8690
# force - append -f to zpool import
8791
# strict - legacy behavior, drop to an emergency shell on failure
@@ -119,8 +123,14 @@ fi
119123

120124
# zbm.timeout= overrides timeout=
121125
menu_timeout=$( get_zbm_arg zbm.timeout timeout )
122-
if [ -n "${menu_timeout}" ]; then
123-
zinfo "setting menu timeout from command line: ${menu_timeout}"
126+
if [ -n "${menu_timeout}" ] ; then
127+
# Ensure that menu_timeout is an integer
128+
if ! [ "${menu_timeout}" -eq "${menu_timeout}" ] >/dev/null 2>&1; then
129+
menu_timeout=10
130+
zinfo "invalid menu timeout, defaulting to ${menu_timeout}"
131+
else
132+
zinfo "setting menu timeout from command line: ${menu_timeout}"
133+
fi
124134
elif get_zbm_bool 0 zbm.show ; then
125135
menu_timeout=-1;
126136
zinfo "forcing display of menu"
@@ -129,6 +139,7 @@ elif get_zbm_bool 0 zbm.skip ; then
129139
zinfo "skipping display of menu"
130140
else
131141
menu_timeout=10
142+
zinfo "defaulting menu timeout to ${menu_timeout}"
132143
fi
133144

134145
zbm_import_delay=$( get_zbm_arg zbm.import_delay )

90zfsbootmenu/lib/kmsg-log-lib.sh

Lines changed: 43 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,86 @@
11
#!/bin/bash
22
# vim: softtabstop=2 shiftwidth=2 expandtab
33

4+
if [ -n "${_ZFSBOOTMENU_KMSG_LOG_LIB}" ]; then
5+
[ -z "${FORCE_RELOAD}" ] && return
6+
else
7+
readonly _ZFSBOOTMENU_KMSG_LOG_LIB=1
8+
fi
9+
410
: "${loglevel:=4}"
511

6-
# arg1: log level
7-
# arg2: log line
12+
# arg1..argN: log line
813
# prints: nothing
9-
# returns: nothing
14+
# returns: 1 if loglevel isn't high enough
1015

11-
zlog() {
16+
zdebug() {
17+
[ ${loglevel:-4} -ge 7 ] || return 1
1218
local prefix trace last lines line lc i
13-
[ -z "${1}" ] && return
14-
[ -z "${2}" ] && return
1519

1620
# Remove everything but new lines from the string, count the length
17-
lines="${2//[!$'\n']/}"
21+
lines="${1//[!$'\n']/}"
1822
lines="${#lines}"
19-
lc=0
2023

2124
while IFS=$'\n' read -r line ; do
22-
# Only add script/function tracing to debug messages
23-
if [ "${1}" -eq 7 ] && [ "${lc}" -eq "${lines}" ] ; then
25+
if [ "${lc:=0}" -eq "${lines}" ] ; then
2426
trace=
2527
last=${#BASH_SOURCE[@]}
26-
for (( i=2 ; i<last ; i++ )) ; do
28+
for (( i=1 ; i<last ; i++ )) ; do
2729
trace="${FUNCNAME[$i]},${BASH_SOURCE[$i]},${BASH_LINENO[$i-1]}${trace:+;}${trace}"
2830
done
29-
prefix="<${1}>ZBM:[$$]|${trace}|"
30-
elif [ "${1}" -eq 7 ] ; then
31-
prefix="<${1}>ZBM:[$$]||"
31+
prefix="<7>ZBM:[$$]|${trace}|"
3232
else
33-
prefix="<${1}>ZFSBootMenu: "
33+
prefix="<7>ZBM:[$$]||"
3434
fi
3535
lc=$(( lc + 1 ))
36-
echo -e "${prefix}${line}" > /dev/kmsg
37-
done <<<"${2}"
36+
echo "${prefix}${line}" > /dev/kmsg
37+
done <<<"${1}"
3838
}
3939

40+
if [ ${loglevel:-4} -ge 7 ] ; then
41+
# Trap errors and send them to the debug handler
42+
traperror() {
43+
zdebug "trapped error from: '${BASH_COMMAND}'"
44+
}
45+
46+
trap traperror ERR
47+
set -o errtrace
48+
fi
49+
4050
# arg1: log line
4151
# prints: nothing
4252
# returns: 1 if loglevel isn't high enough
4353

44-
zdebug() {
45-
[ "${loglevel:-4}" -ge 7 ] || return 1
46-
zlog 7 "$@"
47-
}
48-
4954
zinfo() {
5055
[ "${loglevel:-4}" -ge 6 ] || return 1
51-
zlog 6 "$@"
56+
echo "<6>ZFSBootMenu: $1" > /dev/kmsg
5257
}
5358

59+
# arg1: log line
60+
# prints: nothing
61+
# returns: 1 if loglevel isn't high enough
62+
5463
znotice() {
5564
[ "${loglevel:-4}" -ge 5 ] || return 1
56-
zlog 5 "$@"
65+
echo "<5>ZFSBootMenu: $1" > /dev/kmsg
5766
}
5867

68+
# arg1: log line
69+
# prints: nothing
70+
# returns: 1 if loglevel isn't high enough
71+
5972
zwarn() {
6073
[ "${loglevel:-4}" -ge 4 ] || return 1
6174
: > "${BASE}/have_warnings"
62-
zlog 4 "$@"
75+
echo "<4>ZFSBootMenu: $1" > /dev/kmsg
6376
}
6477

78+
# arg1: log line
79+
# prints: nothing
80+
# returns: 1 if loglevel isn't high enough
81+
6582
zerror() {
6683
[ "${loglevel:-4}" -ge 3 ] || return 1
6784
: > "${BASE}/have_errors"
68-
zlog 3 "$@"
69-
}
70-
71-
traperror() {
72-
zdebug "trapped error from: '${BASH_COMMAND}'"
85+
echo "<3>ZFSBootMenu: $1" > /dev/kmsg
7386
}
74-
75-
if [ "${loglevel:-4}" -eq 7 ] ; then
76-
set -o errtrace
77-
set -o functrace
78-
trap traperror ERR
79-
fi

90zfsbootmenu/lib/zfsbootmenu-core.sh

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -335,9 +335,11 @@ kexec_kernel() {
335335

336336
return 1
337337
else
338-
zdebug "loaded ${mnt}${kernel} and ${mnt}${initramfs} into memory"
339-
zdebug "kernel command line: '${root_prefix}${fs} ${cli_args}'"
340-
zdebug "${output}"
338+
if zdebug ; then
339+
zdebug "loaded ${mnt}${kernel} and ${mnt}${initramfs} into memory"
340+
zdebug "kernel command line: '${root_prefix}${fs} ${cli_args}'"
341+
zdebug "${output}"
342+
fi
341343
fi
342344

343345
umount "${mnt}"
@@ -747,6 +749,7 @@ find_be_kernels() {
747749
labels+=( "$version" )
748750
fi
749751

752+
local ext pfx lbl i
750753
# Use a mess of loops instead better brace expansions to control priorities
751754
for ext in {.img,""}{"",.{gz,bz2,xz,lzma,lz4,lzo,zstd}}; do
752755
for pfx in initramfs initrd; do
@@ -1221,17 +1224,13 @@ has_resume_device() {
12211224
# -m, -r, -e are print in the order they are passed into the function
12221225

12231226
timed_prompt() {
1224-
local prompt delay message
1225-
1226-
delay="30"
1227-
prompt="Press $( colorize green "[RETURN]") or wait $( colorize yellow "%0.${#delay}d" ) seconds to continue"
1228-
message=()
1227+
local prompt delay message opt OPTIND
12291228

1230-
local opt OPTIND
12311229
while getopts "d:p:m:r:e:" opt; do
12321230
case "${opt}" in
12331231
d)
12341232
delay="${OPTARG}"
1233+
[ "${delay}" -gt 0 ] || return 0
12351234
;;
12361235
p)
12371236
prompt="${OPTARG}"
@@ -1250,6 +1249,9 @@ timed_prompt() {
12501249
esac
12511250
done
12521251

1252+
[ -n "${delay}" ] || delay="30"
1253+
[ -n "${prompt}" ] || prompt="Press $( colorize green "[RETURN]") or wait $( colorize yellow "%0.${#delay}d" ) seconds to continue"
1254+
12531255
# Add a blank line between any messages and the prompt
12541256
message+=( "" )
12551257

90zfsbootmenu/lib/zfsbootmenu-kcl.sh

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@ get_zbm_arg() {
203203

204204
for kopt in "$@"; do
205205
kmatch=
206+
207+
# Short-circuit the entire check if the option string isn't in the KCL at all
208+
[[ ${kcl} =~ ${kopt} ]] || continue
209+
206210
while read -r karg; do
207211
# Ignore variables not being tested
208212
kkey="${karg%%=*}"
@@ -238,12 +242,12 @@ get_zbm_bool() {
238242
def="${1}"
239243
shift
240244

241-
case "${def,,}" in
242-
""|no|off|0) def=1;;
243-
*) def=0;;
244-
esac
245-
246-
val="$(get_zbm_arg "$@")" || return ${def}
245+
if ! val="$(get_zbm_arg "$@")"; then
246+
case "${def,,}" in
247+
""|no|off|0) return 1;;
248+
*) return 0;;
249+
esac
250+
fi
247251

248252
case "${val,,}" in
249253
""|no|off|0) return 1;;

90zfsbootmenu/libexec/zfsbootmenu-help

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# vim: softtabstop=2 shiftwidth=2 expandtab
33

44
# shellcheck disable=SC1091
5+
source /lib/profiling-lib.sh >/dev/null 2>&1
6+
source /etc/zfsbootmenu.conf 2>&1 || exit 1
57
source /lib/kmsg-log-lib.sh >/dev/null 2>&1 || exit 1
68
source /lib/zfsbootmenu-core.sh >/dev/null 2>&1 || exit 1
79

90zfsbootmenu/libexec/zfsbootmenu-init

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ trap '' SIGINT
66

77
# Source functional libraries, logging and configuration
88
sources=(
9+
/lib/profiling-lib.sh
910
/etc/zfsbootmenu.conf
1011
/lib/zfsbootmenu-kcl.sh
1112
/lib/zfsbootmenu-core.sh

90zfsbootmenu/libexec/zfsbootmenu-input

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22
trap - SIGINT
33
def_args="$1"
44

5-
function sigint() {
6-
exit 1
7-
}
8-
9-
trap sigint SIGINT
5+
trap 'exit 1' SIGINT
106
read -r -e -i "${def_args}" -p "> " input
117
echo "${input}"
128
exit 0

90zfsbootmenu/libexec/zfsbootmenu-preview

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# vim: softtabstop=2 shiftwidth=2 expandtab
33

44
# shellcheck disable=SC1091
5+
source /lib/profiling-lib.sh >/dev/null 2>&1 || true
6+
source /etc/zfsbootmenu.conf >/dev/null 2>&1 || exit 1
57
source /lib/kmsg-log-lib.sh >/dev/null 2>&1 || exit 1
68
source /lib/zfsbootmenu-core.sh >/dev/null 2>&1 || exit 1
79

90zfsbootmenu/libexec/zfunc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
# vim: softtabstop=2 shiftwidth=2 expandtab
33

44
# shellcheck disable=SC1091
5+
source /lib/profiling-lib.sh >/dev/null 2>&1
6+
source /etc/zfsbootmenu.conf 2>&1 || exit 1
57
source /lib/kmsg-log-lib.sh >/dev/null 2>&1 || exit 1
8+
source /lib/zfsbootmenu-lib.sh >/dev/null 2>&1 || exit 1
69
source /lib/zfsbootmenu-core.sh >/dev/null 2>&1 || exit 1
710

811
# First argument is the function name

0 commit comments

Comments
 (0)