Bash 셸 스크립트 느린 시작을 프로파일링하려면 어떻게 해야 합니까?
Bash 셸을 시작하는 데 최대 3-4초가 소요되는 반면, 다음과 같이 시작할 수 있습니다.--norc
즉시 실행됩니다.
프로파일링을 시작했습니다/etc/bash.bashrc
그리고.~/.bashrc
으로 수으로삽을 return
설명 및 속도 개선을 모색하지만, 정량적인 프로세스가 아니며 효율적이지 않습니다.
Bash 스크립트를 프로파일링하고 실행에 가장 많은 시간이 걸리는 명령을 확인하려면 어떻게 해야 합니까?
GNU GNU가 에는 다음과 같이 하십시오.date
(출력할 수 ), (를 출력할 수 있는 다른 버전)의 부분에 /etc/bash.bashrc
Bash 스크추서적시을작할위치에트"
PS4='+ $(date "+%s.%N")\011 '
exec 3>&2 2>/tmp/bashstart.$$.log
set -x
더하다
set +x
exec 2>&3 3>&-
에의 ~/.bashrc
(또는 추적을 중지할 Bash 스크립트의 섹션 끝에 있음). 그\011
8진수 탭 문자입니다.
를 로받합다니에서 받아야 ./tmp/bashstart.PID.log
실행된 각 명령의 seconds.nanoseconds 타임스탬프를 보여줍니다.한 번과 다음 번의 차이는 개입 단계에 소요된 시간입니다.
범위를 좁히면 이동할 수 있습니다.set -x
나중에 그리고set +x
(또는 여러 관심 섹션을 선택적으로 괄호로 묶습니다).
GNU만큼 ,date
의 나노초, Bash 5에는 시간을 마이크로초 단위로 제공하는 변수가 포함되어 있습니다. 파일을 하는 것을 할 수 GNU가 Mac에서 합니다.date
물론, 당신이 배시 5를 가지고 있는 한.설정 변경:PS4
:
PS4='+ $EPOCHREALTIME\011 '
@pawamoy가 지적했듯이, 당신은 다음을 사용할 수 있습니다.BASH_XTRACEFD
Bash 4.1 이상을 사용하는 경우 추적의 출력을 별도의 파일 설명자로 보냅니다.이 답변에서:
#!/bin/bash
exec 5> command.txt
BASH_XTRACEFD="5"
echo -n "hello "
set -x
echo -n world
set +x
echo "!"
" " " "로합니다.command.txt
stdout
그리고.stdout
정상적으로 출력되거나 별도로 리디렉션됩니다.
Bash 프로파일링(4개 답변)
이 문서를 읽고 프로파일링이 중요한 단계이기 때문에 스택 오버플로 문제에 대한 테스트와 조사를 수행했으며 이미 답변을 게시했습니다.
다음은 네 가지 이상의 답변입니다.
첫 번째는 @Dennis Williamson의 아이디어를 기반으로 하지만 자원 소비는 훨씬 적습니다.
두 번째는 나의 것이었습니다 (이것 이전;)
세 번째는 @fgm의 답변을 기반으로 하지만, 더 정확합니다.
은 최의용법을 합니다.
script
,scriptreplay
타이밍 파일입니다.마지막으로, 마지막으로 성능을 약간 비교합니다.
사용set -x
그리고.date
하지만 제한된 포크로
@Dennis Williamson의 아이디어를 참고하면, 다음 구문을 사용하면 초기 포크 하나에서 세 개의 명령어만 사용할 수 있습니다.
exec 3>&2 2> >(tee /tmp/sample-time.$$.log |
sed -u 's/^.*$/now/' |
date -f - +%s.%N >/tmp/sample-time.$$.tim)
set -x
하면 이게하실다니됩행이 됩니다.date
» 작동 빠른 .작동 방식을 보여주는 빠른 데모/테스트가 있습니다.
for i in {1..4};do echo now;sleep .05;done| date -f - +%N
샘플 스크립트:
#!/bin/bash
exec 3>&2 2> >( tee /tmp/sample-$$.log |
sed -u 's/^.*$/now/' |
date -f - +%s.%N >/tmp/sample-$$.tim)
set -x
for ((i=3;i--;));do sleep .1;done
for ((i=2;i--;))
do
tar -cf /tmp/test.tar -C / bin
gzip /tmp/test.tar
rm /tmp/test.tar.gz
done
set +x
exec 2>&3 3>&-
이 스크립트를 실행하면 두 개의 파일이 만들어집니다./tmp/sample-XXXX.log
그리고./tmp/sample-XXXX.tim
(여기서 XXXX는 실행 중인 스크립트의 프로세스 ID입니다.)
다음을 사용하여 표시할 수 있습니다.paste
:
paste tmp/sample-XXXX.{tim,log}
또는 다른 시간을 계산할 수도 있습니다.
paste <(
while read tim ;do
crt=000000000$((${tim//.}-10#0$last))
printf "%12.9f\n" ${crt:0:${#crt}-9}.${crt:${#crt}-9}
last=${tim//.}
done < sample-time.24804.tim
) sample-time.24804.log
1388487534.391309713 + (( i=3 ))
0.000080807 + (( i-- ))
0.000008312 + sleep .1
0.101304843 + (( 1 ))
0.000032616 + (( i-- ))
0.000007124 + sleep .1
0.101251684 + (( 1 ))
0.000033036 + (( i-- ))
0.000007054 + sleep .1
0.104013813 + (( 1 ))
0.000026959 + (( i-- ))
0.000006915 + (( i=2 ))
0.000006635 + (( i-- ))
0.000006844 + tar -cf /tmp/test.tar -C / bin
0.022655107 + gzip /tmp/test.tar
0.637042668 + rm /tmp/test.tar.gz
0.000823649 + (( 1 ))
0.000011314 + (( i-- ))
0.000006915 + tar -cf /tmp/test.tar -C / bin
0.016084482 + gzip /tmp/test.tar
0.627798263 + rm /tmp/test.tar.gz
0.001294946 + (( 1 ))
0.000023187 + (( i-- ))
0.000006845 + set +x
또는 두 개의 열에:
paste <(
while read tim ;do
[ -z "$last" ] && last=${tim//.} && first=${tim//.}
crt=000000000$((${tim//.}-10#0$last))
ctot=000000000$((${tim//.}-10#0$first))
printf "%12.9f %12.9f\n" ${crt:0:${#crt}-9}.${crt:${#crt}-9} \
${ctot:0:${#ctot}-9}.${ctot:${#ctot}-9}
last=${tim//.}
done < sample-time.24804.tim
) sample-time.24804.log
렌더링할 수 있음:
0.000000000 0.000000000 + (( i=3 ))
0.000080807 0.000080807 + (( i-- ))
0.000008312 0.000089119 + sleep .1
0.101304843 0.101393962 + (( 1 ))
0.000032616 0.101426578 + (( i-- ))
0.000007124 0.101433702 + sleep .1
0.101251684 0.202685386 + (( 1 ))
0.000033036 0.202718422 + (( i-- ))
0.000007054 0.202725476 + sleep .1
0.104013813 0.306739289 + (( 1 ))
0.000026959 0.306766248 + (( i-- ))
0.000006915 0.306773163 + (( i=2 ))
0.000006635 0.306779798 + (( i-- ))
0.000006844 0.306786642 + tar -cf /tmp/test.tar -C / bin
0.022655107 0.329441749 + gzip /tmp/test.tar
0.637042668 0.966484417 + rm /tmp/test.tar.gz
0.000823649 0.967308066 + (( 1 ))
0.000011314 0.967319380 + (( i-- ))
0.000006915 0.967326295 + tar -cf /tmp/test.tar -C / bin
0.016084482 0.983410777 + gzip /tmp/test.tar
0.627798263 1.611209040 + rm /tmp/test.tar.gz
0.001294946 1.612503986 + (( 1 ))
0.000023187 1.612527173 + (( i-- ))
0.000006845 1.612534018 + set +x
사용trap debug
그리고./proc/timer_list
최신 GNU/리눅스 커널에서 포크 없이.
GNU/리눅스의 최근 커널에서 다음을 찾을 수 있습니다./proc
이름이 파이지정입니다.timer_list
:
grep 'now at\|offset' /proc/timer_list
now at 5461935212966259 nsecs
.offset: 0 nsecs
.offset: 1383718821564493249 nsecs
.offset: 0 nsecs
은 여서현시다합다계니입의음의 입니다.5461935212966259 + 1383718821564493249
하지만 나노초 안에.
따라서 경과 시간을 계산할 때 오프셋을 알 필요가 없습니다.
이러한 작업을 위해 다음 구문을 사용하여 elap.bash(V2)를 작성했습니다.
source elap.bash-v2
또는
. elap.bash-v2 init
(전체 구문은 주석 참조)
따라서 스크립트 맨 위에 다음 행을 추가하기만 하면 됩니다.
. elap.bash-v2 trap2
약간의 샘플:
#!/bin/bash
. elap.bash-v2 trap
for ((i=3;i--;));do sleep .1;done
elapCalc2
elapShowTotal \\e[1mfirst total\\e[0m
for ((i=2;i--;))
do
tar -cf /tmp/test.tar -C / bin
gzip /tmp/test.tar
rm /tmp/test.tar.gz
done
trap -- debug
elapTotal \\e[1mtotal time\\e[0m
호스트에서 렌더링:
0.000947481 Starting
0.000796900 ((i=3))
0.000696956 ((i--))
0.101969242 sleep .1
0.000812478 ((1))
0.000755067 ((i--))
0.103693305 sleep .1
0.000730482 ((1))
0.000660360 ((i--))
0.103565001 sleep .1
0.000719516 ((1))
0.000671325 ((i--))
0.000754856 elapCalc2
0.316018113 first total
0.000754787 elapShowTotal \e[1mfirst total\e[0m
0.000711275 ((i=2))
0.000683408 ((i--))
0.075673816 tar -cf /tmp/test.tar -C / bin
0.596389329 gzip /tmp/test.tar
0.006565188 rm /tmp/test.tar.gz
0.000830217 ((1))
0.000759466 ((i--))
0.024783966 tar -cf /tmp/test.tar -C / bin
0.604119903 gzip /tmp/test.tar
0.005172940 rm /tmp/test.tar.gz
0.000952299 ((1))
0.000827421 ((i--))
1.635788924 total time
1.636657204 EXIT
용사를 합니다.trap2
에 trap
소스 명령에 대한 인수로:
#!/bin/bash
. elap.bash-v2 trap2
...
두 개의 열을 마지막 명령과 총계로 렌더링합니다.
0.000894541 0.000894541 Starting
0.001306122 0.002200663 ((i=3))
0.001929397 0.004130060 ((i--))
0.103035812 0.107165872 sleep .1
0.000875613 0.108041485 ((1))
0.000813872 0.108855357 ((i--))
0.104954517 0.213809874 sleep .1
0.000900617 0.214710491 ((1))
0.000842159 0.215552650 ((i--))
0.104846890 0.320399540 sleep .1
0.000899082 0.321298622 ((1))
0.000811708 0.322110330 ((i--))
0.000879455 0.322989785 elapCalc2
0.322989785 first total
0.000906692 0.323896477 elapShowTotal \e[1mfirst total\e[0m
0.000820089 0.324716566 ((i=2))
0.000773782 0.325490348 ((i--))
0.024752613 0.350242961 tar -cf /tmp/test.tar -C / bin
0.596199363 0.946442324 gzip /tmp/test.tar
0.003007128 0.949449452 rm /tmp/test.tar.gz
0.000791452 0.950240904 ((1))
0.000779371 0.951020275 ((i--))
0.030519702 0.981539977 tar -cf /tmp/test.tar -C / bin
0.584155405 1.565695382 gzip /tmp/test.tar
0.003058674 1.568754056 rm /tmp/test.tar.gz
0.000955093 1.569709149 ((1))
0.000919964 1.570629113 ((i--))
1.571516599 total time
0.001723708 1.572352821 EXIT
트레이스 사용
네, 트레이스가 할 수 있습니다.
strace -q -f -s 10 -ttt sample-script 2>sample-script-strace.log
하지만 그것은 많은 것들을 만들 수 있습니다!
wc sample-script-strace.log
6925 57637 586518 sample-script-strace.log
보다 제한된 명령 사용:
strace -f -s 10 -ttt -eopen,access,read,write ./sample-script 2>sample-script-strace.log
더 가벼운 로그를 덤프합니다.
4519 36695 374453 sample-script-strace.log
검색하는 내용에 따라 다음과 같이 제한할 수 있습니다.
strace -f -s 10 -ttt -eaccess,open ./sample-script 2>&1 | wc
189 1451 13682
그것들을 읽는 것은 조금 더 어려울 것입니다.
{
read -a first
first=${first//.}
last=$first
while read tim line;do
crt=000000000$((${tim//.}-last))
ctot=000000000$((${tim//.}-first))
printf "%9.6f %9.6f %s\n" ${crt:0:${#crt}-6}.${crt:${#crt}-6} \
${ctot:0:${#ctot}-6}.${ctot:${#ctot}-6} "$line"
last=${tim//.}
done
} < <(
sed </tmp/sample-script.strace -e '
s/^ *//;
s/^\[[^]]*\] *//;
/^[0-9]\{4\}/!d
')
0.000110 0.000110 open("/lib/x86_64-linux-gnu/libtinfo.so.5", O_RDONLY) = 4
0.000132 0.000242 open("/lib/x86_64-linux-gnu/libdl.so.2", O_RDONLY) = 4
0.000121 0.000363 open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY) = 4
0.000462 0.000825 open("/dev/tty", O_RDWR|O_NONBLOCK) = 4
0.000147 0.000972 open("/usr/lib/locale/locale-archive", O_RDONLY) = 4
...
0.000793 1.551331 open("/etc/ld.so.cache", O_RDONLY) = 4
0.000127 1.551458 open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY) = 4
0.000545 1.552003 open("/usr/lib/locale/locale-archive", O_RDONLY) = 4
0.000439 1.552442 --- SIGCHLD (Child exited) @ 0 (0) ---
원래의 bash 스크립트를 따르는 것은 쉽지 않습니다...
사용script
,scriptreplay
및 타이밍 파일
디버깅 스크립트의 경우 다음을 자주 사용합니다.
script -t script.log 2>script.tim -c 'bash -x /path/script.sh'
BSD Utils의 일부로서,script
)scriptreplay
)는 bash를 프로파일링하는 데 사용할 수 있는 매우 오래된 도구로, 설치 공간이 매우 작습니다.
간단한 데모:
script -t script.log 2>script.tim -c 'bash -x -c "
for ((i=3;i--;));do sleep .1;done
for ((i=2;i--;)) ;do
tar -cf /tmp/test.tar -C / bin
gzip /tmp/test.tar
rm /tmp/test.tar.gz
done
"'
생산 예정:
Script started on Fri Mar 25 08:29:37 2016
+ (( i=3 ))
+ (( i-- ))
+ sleep .1
+ (( 1 ))
+ (( i-- ))
+ sleep .1
+ (( 1 ))
+ (( i-- ))
+ sleep .1
+ (( 1 ))
+ (( i-- ))
+ (( i=2 ))
+ (( i-- ))
+ tar -cf /tmp/test.tar -C / bin
+ gzip /tmp/test.tar
+ rm /tmp/test.tar.gz
+ (( 1 ))
+ (( i-- ))
+ tar -cf /tmp/test.tar -C / bin
+ gzip /tmp/test.tar
+ rm /tmp/test.tar.gz
+ (( 1 ))
+ (( i-- ))
Script done on Fri Mar 25 08:29:39 2016
두 개의 파일을 생성합니다.
ls -l script.*
-rw-r--r-- 1 user user 450 Mar 25 08:29 script.log
-rw-r--r-- 1 user user 177 Mar 25 08:29 script.tim
파script.log
와 모든트스포니다함합를을 합니다.script.tim
타이밍 파일입니다.
head -n 4 script.*
==> script.log <==
Script started on Fri Mar 25 08:29:37 2016
+ (( i=3 ))
+ (( i-- ))
+ sleep .1
==> script.tim <==
0.435331 11
0.000033 2
0.000024 11
0.000010 2
로그 파일의 첫 번째 줄과 마지막 줄을 사용하거나 타이밍 파일의 시간을 요약하여 실행된 총 시간을 확인할 수 있습니다.
head -n1 script.log ;tail -n1 script.log
Script started on Fri Mar 25 08:29:37 2016
Script done on Fri Mar 25 08:29:39 2016
sed < script.tim 's/ .*$//;H;${x;s/\n/+/g;s/^\+//;p};d' | bc -l
2.249755
타이밍 파일에서 두 번째 값은 해당 로그 파일의 다음 바이트 수입니다.이를 통해 가속 계수를 사용하여 로그 파일을 선택적으로 재생할 수 있습니다.
scriptreplay script.{tim,log}
또는
scriptreplay script.{tim,log} 5
또는
scriptreplay script.{tim,log} .2
시간과 명령을 나란히 표시하는 것도 조금 더 복잡합니다.
exec 4<script.log
read -u 4 line
echo $line ;while read tim char;do
read -u 4 -N $char -r -s line
echo $tim $line
done < script.tim &&
while read -u 4 line;do
echo $line
done;exec 4<&-
Script started on Fri Mar 25 08:28:51 2016
0.558012 + (( i=3 ))
0.000053
0.000176 + (( i-- ))
0.000015
0.000059 + sleep .1
0.000015
+ sleep .1) + (( 1 ))
+ sleep .1) + (( 1 ))
+ tar -cf /tmp/test.tar -C / bin
0.035024 + gzip /tmp/test.tar
0.793846 + rm /tmp/test.tar.gz
+ tar -cf /tmp/test.tar -C / bin
0.024971 + gzip /tmp/test.tar
0.729062 + rm /tmp/test.tar.gz
+ (( i-- )) + (( 1 ))
Script done on Fri Mar 25 08:28:53 2016
스크립트 재생에 대한 추가 작업:"script" 명령으로 생성된 타이밍 및 유형 스크립트 파일을 조작하는 방법은 무엇입니까?
시험 및 결론
테스트를 위해 Bash complex Hello, World!에서 두 번째 샘플을 다운로드했습니다.호스트에서 이 스크립트를 완료하는 데 약 0.72초가 걸립니다.
스크립트의 맨 위에 다음 중 하나를 추가했습니다.
타고
elap.bash
를 수행#!/bin/bash source elap.bash-v2 trap2 eval "BUNCHS=(" $(perl <<EOF | gunzip ...
타고
set -x
그리고.PS4
#!/bin/bash PS4='+ $(date "+%s.%N")\011 ' exec 3>&2 2>/tmp/bashstart.$$.log set -x eval "BUNCHS=(" $(perl <<EOF | gunzip ...
타고
set -x
및 initial fork to long exec 명령어#!/bin/bash exec 3>&2 2> >(tee /tmp/sample-time.$$.log | sed -u 's/^.*$/now/' | date -f - +%s.%N >/tmp/sample-time.$$.tim) set -x eval "BUNCHS=(" $(perl <<EOF | gunzip
타고
script
)set +x
)script -t helloworld.log 2>helloworld.tim -c ' bash -x complex_helloworld-2.sh' >/dev/null
시대
실행 시간 비교(내 호스트):
- 직접 0.72초
- 1랩 13.18초
- 설정 + 날짜@PS454.61초
- 세트 + 포크 1개 1.45초
- 스크립트 및 타이밍 파일 2.19초
- 4.47초 추적
출력
- 타고
elap.bash
를 수행
0.000950277 0.000950277 Starting
0.007618964 0.008569241 eval "BUNCHS=(" $(perl <<EOF | gunzi
0.005259953 0.013829194 BUNCHS=("2411 1115 -13 15 33 -3 15 1
0.010945070 0.024774264 MKey="V922/G/,2:"
0.001050990 0.025825254 export RotString=""
0.004724348 0.030549602 initRotString
0.001322184 0.031871786 for bunch in "${BUNCHS[@]}"
0.000768893 0.032640679 out=""
0.001008242 0.033648921 bunchArray=($bunch)
0.000741095 0.034390016 ((k=0))
- 타고
set -x
그리고.PS4
++ 1388598366.536099290 perl
++ 1388598366.536169132 gunzip
+ 1388598366.552794757 eval 'BUNCHS=(' '"2411' 1115 -13 15 33 -3 15 1
++ 1388598366.555001983 BUNCHS=("2411 1115 -13 15 33 -3 15 13111 -6 1
+ 1388598366.557551018 MKey=V922/G/,2:
+ 1388598366.558316839 export RotString=
+ 1388598366.559083848 RotString=
+ 1388598366.560165147 initRotString
+ 1388598366.560942633 local _i _char
+ 1388598366.561706988 RotString=
- 타고
set -x
그리고 long exec 명령에 대한 초기 포크(그리고 내 두 번째.paste
샘플 스크립트)
0.000000000 0.000000000 ++ perl
0.008141159 0.008141159 ++ gunzip
0.000007822 0.008148981 + eval 'BUNCHS=(' '"2411' 1115 -13 15 33 -3
0.000006216 0.008155197 ++ BUNCHS=("2411 1115 -13 15 33 -3 15 13111
0.000006216 0.008161413 + MKey=V922/G/,2:
0.000006076 0.008167489 + export RotString=
0.000006007 0.008173496 + RotString=
0.000006006 0.008179502 + initRotString
0.000005937 0.008185439 + local _i _char
0.000006006 0.008191445 + RotString=
- 타고
strace
0.000213 0.000213 brk(0) = 0x17b6000
0.000044 0.000257 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
0.000047 0.000304 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7faf1c0dc000
0.000040 0.000344 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
0.000040 0.000384 open("/etc/ld.so.cache", O_RDONLY) = 4
...
0.000024 4.425049 close(10) = 0
0.000042 4.425091 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
0.000028 4.425119 read(255, "", 4409) = 0
0.000058 4.425177 exit_group(0) = ?
- 타고
script
Le script a débuté sur ven 25 mar 2016 09:18:35 CET
0.667160 ++ gunzip
0.000025
0.000948 ++ perl
0.000011
0.005338 + eval 'BUNCHS=(' '"2411' 1115 -13 15 33 -3 15 13111 -6 1 111 4
0.000044 1223 15 3311 121121 17 3311 121121 1223 3311 121121 17 3311 121
0.000175 ++ BUNCHS=("2411 1115 -13 15 33 -3 15 13111 -6 15 1114 15 12211
0.000029 1 1321 12211 412 21211 33 21211 -2 15 2311 11121 232 121111 122
0.000023 4 3311 121121 12221 3311 121121 12221 3311 121121 1313 -6 15 33
결론
글쎄요! 순수 Bash 스크립트가 각 명령에 대해 현재까지 forking하는 것보다 빠르다면, 이것을 사용하는 것은 각 명령에 대한 몇 가지 작업을 의미합니다.
로깅 및 저장을 위해 독립적인 프로세스를 전용하는 방법이 분명히 더 효율적입니다.
strace는 더 상세하지만 읽기 어려운 흥미로운 방법입니다.
script
,와 함께scriptreplay
가속 계수도 매우 좋지만, 이와 같은 정밀도는 프로세스 실행 대신 콘솔 교환을 기반으로 하지만 매우 가볍고 효율적입니다(동일한 목표, 동일한 용도가 아님).
마지막으로, 저는 가독성과 성능이 더 효율적일수록, 이 대답의 첫 번째는, 그러나 결국, 구체적인 경우에 따라, 저는 가끔 사용합니다.strace
및/는script
도 마찬가지야
시스템 호출을 추적하는 데 종종 도움이 됩니다.
strace -c -f ./script.sh
설명서에서:
-c 각 시스템 호출에 대한 시간, 호출 및 오류를 카운트하고 프로그램 종료 시 요약을 보고합니다.
-f 하위 프로세스 추적...
이것은 정확히 당신이 원하는 것이 아니며 라인 지향 프로파일러가 당신에게 보여줄 것은 아니지만 일반적으로 핫 스팟을 찾는 데 도움이 됩니다.
다▁at▁를 볼 수 있습니다.trap
DEBUG 조건이 있는 명령입니다.명령과 함께 실행할 명령을 설정하는 방법이 있습니다.답에 대한 참고 사항을 참조하십시오.
스크립트 앞에 다음을 추가합니다.
N=`date +%s%N`; export PS4='+[$(((`date +%s%N`-$N)/1000000))ms][${BASH_SOURCE}:${LINENO}]: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }';
exec 19>$HOME/logfile
BASH_XTRACEFD=19
set -x
출력 파일에는 밀리초 단위로 명령이 나열됩니다.
$ tailf ~/logfile
++[389426ms][/home/subtleseeker/.iterm2_shell_integration.bash:96]: __bp_preexec_invoke_exec(): type -t preexec
++[389428ms][/home/subtleseeker/.iterm2_shell_integration.bash:113]: __bp_preexec_invoke_exec(): __bp_set_ret_value 0 /home/subtleseeker/.bashrc
++[389431ms][/home/subtleseeker/.iterm2_shell_integration.bash:1]: __bp_set_ret_value(): return 0
+[389433ms][:69]: tailf /home/subtleseeker/logfile
시작이 초 범위에 있고 느린 단일 명령인 경우 매우 멍청한 방법이 있습니다.려달을 합니다.bash -x
또는bash -lx
느림) 초기화를 한 한 를 채웁니다.그런 다음 뒤로 스크롤하여 빈 줄이 가장 많은 명령을 확인합니다.그건 당신의 느린 명령입니다.
앨런 하그리브스의 이 게시물은 DTrace 공급자를 사용하여 본 셸 스크립트를 프로파일링하는 방법에 대해 설명합니다.Solaris 및 OpenSolaris에서 작동하는 것으로 알고 있습니다(/bin/shDtrace Provider 참조).
따라서 다음과 같은 DTrace 스크립트(sh_flowtime.d
원본 기준 GH)가 주어집니다.
#!/usr/sbin/dtrace -Zs
#pragma D option quiet
#pragma D option switchrate=10
dtrace:::BEGIN
{
depth = 0;
printf("%s %-20s %-22s %s %s\n", "C", "TIME", "FILE", "DELTA(us)", "NAME");
}
sh*:::function-entry
{
depth++;
printf("%d %-20Y %-22s %*s-> %s\n", cpu, walltimestamp,
basename(copyinstr(arg0)), depth*2, "", copyinstr(arg1));
}
sh*:::function-return
{
printf("%d %-20Y %-22s %*s<- %s\n", cpu, walltimestamp,
basename(copyinstr(arg0)), depth*2, "", copyinstr(arg1));
depth--;
}
sh*:::builtin-entry
{
printf("%d %-20Y %-22s %*s > %s\n", cpu, walltimestamp,
basename(copyinstr(arg0)), depth*2, "", copyinstr(arg1));
}
sh*:::command-entry
{
printf("%d %-20Y %-22s %*s | %s\n", cpu, walltimestamp,
basename(copyinstr(arg0)), depth*2, "", copyinstr(arg1));
}
델타 시간을 포함하여 함수 흐름을 추적할 수 있습니다.
샘플 출력:
# ./sh_flowtime.d
C TIME FILE DELTA(us) -- NAME
0 2007 Aug 10 18:52:51 func_abc.sh 0 -> func_a
0 2007 Aug 10 18:52:51 func_abc.sh 54 > echo
0 2007 Aug 10 18:52:52 func_abc.sh 1022880 | sleep
0 2007 Aug 10 18:52:52 func_abc.sh 34 -> func_b
0 2007 Aug 10 18:52:52 func_abc.sh 44 > echo
0 2007 Aug 10 18:52:53 func_abc.sh 1029963 | sleep
0 2007 Aug 10 18:52:53 func_abc.sh 44 -> func_c
0 2007 Aug 10 18:52:53 func_abc.sh 43 > echo
0 2007 Aug 10 18:52:54 func_abc.sh 1029863 | sleep
0 2007 Aug 10 18:52:54 func_abc.sh 33 <- func_c
0 2007 Aug 10 18:52:54 func_abc.sh 14 <- func_b
0 2007 Aug 10 18:52:54 func_abc.sh 7 <- func_a
다음 용중사를 합니다.sort -nrk7
명령을 사용하면 가장 많이 사용하는 통화를 표시하도록 출력을 정렬할 수 있습니다.
다른 셸에 사용할 수 있는 공급자 프로브가 있는지 알 수 없습니다. 따라서 일부 연구(GitHub 검색)를 수행하거나 시간을 투자하고 싶다면 기존 sh 예제를 기반으로 작성할 수 있습니다(shD Trace Provider 활성화 방법 참조).
-x, 시간, xtrace, bash-x,set -x
그리고.set +x
(2.3. Bash 스크립트 디버깅) 스크립트를 디버깅하는 일반적인 방법은 그대로 유지됩니다.
그럼에도 불구하고, 우리의 시야를 넓히거나 자원의 사용을 보다 정확하게 제어하기 위해 Linux에서 사용할 수 있는 디버깅 및 프로파일링 시스템을 확인할 수 있습니다. 예를 들어, Valgrind는 메모리 디버깅을 위해, sysprof는 전체 시스템을 프로파일링합니다.
sysprof의 경우:
sysprof를 사용하면 다중 스레드 애플리케이션이나 다중 프로세스 애플리케이션을 포함하여 시스템에서 실행 중인 모든 애플리케이션을 프로파일링할 수 있습니다.
그런 다음 관심 있는 하위 프로세스의 분기를 선택합니다.
Valgrind의 경우:
체육관이 조금 더 늘어나면, 우리가 보통 바이너리에서 설치하는 일부 프로그램(예: OpenOffice)을 Valgrind에게 보여줄 수 있을 것 같습니다.
Valgrind의 FAQ에서 명시적으로 요청할 경우 Valgrind가 하위 프로세스를 프로파일링할 것이라고 읽을 수 있습니다.
기본적으로 프로파일이 최상위 프로세스만 추적하므로 프로그램이 셸 스크립트, 펄 스크립트 또는 유사한 것에 의해 시작되면 Valgrind가 셸 또는 펄 인터프리터 또는 유사한 것을 추적합니다.
이 옵션을 활성화하면 다음과 같이 수행됩니다.
--trace-children=yes
추가 참조:
- 발그랜드 설명서.
- KCachegrind 및 Callgrind 인터페이스에 대한 몇 가지 뉴스 또는 CERN의 Wiki에서 보고된 것처럼 여전히 사용 중입니다.
- GDB에 대한 GDB 설명서.이는 스크립트에서 호출되는 C 및 C++ 프로그램을 프로파일링하는 데 유용한 결과를 초래할 수 있습니다.
언급URL : https://stackoverflow.com/questions/5014823/how-can-i-profile-a-bash-shell-script-slow-startup
'programing' 카테고리의 다른 글
git-mv의 목적은 무엇입니까? (0) | 2023.05.28 |
---|---|
jQuery를 사용하여 롤오버 시 이미지 소스 변경 (0) | 2023.05.28 |
Twitter 부트스트랩 원격 모달이 매번 동일한 콘텐츠를 표시함 (0) | 2023.05.28 |
iOS 7: 상태 표시줄 아래에 UITableView 표시 (0) | 2023.05.28 |
Xcode 9 GM - WKWebView 이전 버전에서는 코딩 지원이 중단되었습니다. (0) | 2023.05.28 |