Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   1) #!/bin/bash
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   2) # SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   3) # (c) 2014, Sasha Levin <sasha.levin@oracle.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   4) #set -x
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   5) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   6) if [[ $# < 1 ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   7) 	echo "Usage:"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   8) 	echo "	$0 -r <release> | <vmlinux> [base path] [modules path]"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   9) 	exit 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  10) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  11) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  12) if [[ $1 == "-r" ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  13) 	vmlinux=""
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  14) 	basepath="auto"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  15) 	modpath=""
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  16) 	release=$2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  17) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  18) 	for fn in {,/usr/lib/debug}/boot/vmlinux-$release{,.debug} /lib/modules/$release{,/build}/vmlinux ; do
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  19) 		if [ -e "$fn" ] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  20) 			vmlinux=$fn
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  21) 			break
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  22) 		fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  23) 	done
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  24) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  25) 	if [[ $vmlinux == "" ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  26) 		echo "ERROR! vmlinux image for release $release is not found" >&2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  27) 		exit 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  28) 	fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  29) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  30) 	vmlinux=$1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  31) 	basepath=${2-auto}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  32) 	modpath=$3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  33) 	release=""
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  34) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  35) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  36) declare -A cache
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  37) declare -A modcache
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  38) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  39) find_module() {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  40) 	if [[ "$modpath" != "" ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  41) 		for fn in $(find "$modpath" -name "${module//_/[-_]}.ko*") ; do
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  42) 			if readelf -WS "$fn" | grep -qwF .debug_line ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  43) 				echo $fn
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  44) 				return
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  45) 			fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  46) 		done
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  47) 		return 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  48) 	fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  49) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  50) 	modpath=$(dirname "$vmlinux")
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  51) 	find_module && return
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  52) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  53) 	if [[ $release == "" ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  54) 		release=$(gdb -ex 'print init_uts_ns.name.release' -ex 'quit' -quiet -batch "$vmlinux" | sed -n 's/\$1 = "\(.*\)".*/\1/p')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  55) 	fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  56) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  57) 	for dn in {/usr/lib/debug,}/lib/modules/$release ; do
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  58) 		if [ -e "$dn" ] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  59) 			modpath="$dn"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  60) 			find_module && return
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  61) 		fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  62) 	done
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  63) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  64) 	modpath=""
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  65) 	return 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  66) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  67) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  68) parse_symbol() {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  69) 	# The structure of symbol at this point is:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  70) 	#   ([name]+[offset]/[total length])
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  71) 	#
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  72) 	# For example:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  73) 	#   do_basic_setup+0x9c/0xbf
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  74) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  75) 	if [[ $module == "" ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  76) 		local objfile=$vmlinux
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  77) 	elif [[ "${modcache[$module]+isset}" == "isset" ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  78) 		local objfile=${modcache[$module]}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  79) 	else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  80) 		local objfile=$(find_module)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  81) 		if [[ $objfile == "" ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  82) 			echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  83) 			return
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  84) 		fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  85) 		modcache[$module]=$objfile
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  86) 	fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  87) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  88) 	# Remove the englobing parenthesis
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  89) 	symbol=${symbol#\(}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  90) 	symbol=${symbol%\)}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  91) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  92) 	# Strip segment
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  93) 	local segment
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  94) 	if [[ $symbol == *:* ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  95) 		segment=${symbol%%:*}:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  96) 		symbol=${symbol#*:}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  97) 	fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  98) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  99) 	# Strip the symbol name so that we could look it up
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) 	local name=${symbol%+*}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) 	# Use 'nm vmlinux' to figure out the base address of said symbol.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) 	# It's actually faster to call it every time than to load it
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) 	# all into bash.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) 	if [[ "${cache[$module,$name]+isset}" == "isset" ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) 		local base_addr=${cache[$module,$name]}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) 	else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) 		local base_addr=$(nm "$objfile" | awk '$3 == "'$name'" && ($2 == "t" || $2 == "T") {print $1; exit}')
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) 		if [[ $base_addr == "" ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) 			# address not found
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) 			return
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) 		fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) 		cache[$module,$name]="$base_addr"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) 	fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) 	# Let's start doing the math to get the exact address into the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) 	# symbol. First, strip out the symbol total length.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) 	local expr=${symbol%/*}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) 	# Now, replace the symbol name with the base address we found
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) 	# before.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) 	expr=${expr/$name/0x$base_addr}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) 	# Evaluate it to find the actual address
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) 	expr=$((expr))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) 	local address=$(printf "%x\n" "$expr")
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) 	# Pass it to addr2line to get filename and line number
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) 	# Could get more than one result
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) 	if [[ "${cache[$module,$address]+isset}" == "isset" ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) 		local code=${cache[$module,$address]}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) 	else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) 		local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address")
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) 		cache[$module,$address]=$code
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) 	fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) 	# addr2line doesn't return a proper error code if it fails, so
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) 	# we detect it using the value it prints so that we could preserve
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) 	# the offset/size into the function and bail out
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) 	if [[ $code == "??:0" ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) 		return
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) 	fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) 	# Strip out the base of the path on each line
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) 	code=$(while read -r line; do echo "${line#$basepath/}"; done <<< "$code")
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) 	# In the case of inlines, move everything to same line
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) 	code=${code//$'\n'/' '}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) 	# Replace old address with pretty line numbers
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) 	symbol="$segment$name ($code)"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) decode_code() {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) 	local scripts=`dirname "${BASH_SOURCE[0]}"`
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) 	echo "$1" | $scripts/decodecode
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) handle_line() {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) 	local words
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) 	# Tokenize
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) 	read -a words <<<"$1"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) 	# Remove hex numbers. Do it ourselves until it happens in the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) 	# kernel
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) 	# We need to know the index of the last element before we
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) 	# remove elements because arrays are sparse
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) 	local last=$(( ${#words[@]} - 1 ))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) 	for i in "${!words[@]}"; do
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) 		# Remove the address
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) 		if [[ ${words[$i]} =~ \[\<([^]]+)\>\] ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) 			unset words[$i]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) 		fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) 		# Format timestamps with tabs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) 		if [[ ${words[$i]} == \[ && ${words[$i+1]} == *\] ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) 			unset words[$i]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) 			words[$i+1]=$(printf "[%13s\n" "${words[$i+1]}")
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) 		fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) 	done
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) 	if [[ ${words[$last]} =~ \[([^]]+)\] ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) 		module=${words[$last]}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) 		module=${module#\[}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) 		module=${module%\]}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) 		symbol=${words[$last-1]}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) 		unset words[$last-1]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) 	else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) 		# The symbol is the last element, process it
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) 		symbol=${words[$last]}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) 		module=
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) 	fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) 	unset words[$last]
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) 	parse_symbol # modifies $symbol
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) 	# Add up the line number to the symbol
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) 	echo "${words[@]}" "$symbol $module"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) if [[ $basepath == "auto" ]] ; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) 	module=""
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) 	symbol="kernel_init+0x0/0x0"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) 	parse_symbol
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) 	basepath=${symbol#kernel_init (}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) 	basepath=${basepath%/init/main.c:*)}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) while read line; do
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) 	# Let's see if we have an address in the line
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) 	if [[ $line =~ \[\<([^]]+)\>\] ]] ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) 	   [[ $line =~ [^+\ ]+\+0x[0-9a-f]+/0x[0-9a-f]+ ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) 		# Translate address to line numbers
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) 		handle_line "$line"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) 	# Is it a code line?
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) 	elif [[ $line == *Code:* ]]; then
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) 		decode_code "$line"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) 	else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) 		# Nothing special in this line, show it as is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) 		echo "$line"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) 	fi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) done