如何配置bash shell脚本慢启动?

时间:2011-02-16 09:43:51

标签: bash profiling

我的bash shell需要3-4秒才能启动,而如果我用--norc启动它会立即运行。

我开始通过手动插入/etc/bash.bashrc语句并寻求提高速度来“分析”~/.bashrcreturn,但这不是一个定量过程而且效率不高。

如何配置我的bash脚本并查看哪些命令需要花费大量时间来启动?

7 个答案:

答案 0 :(得分:115)

如果您有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是八进制制表符。

您应该在/tmp/bashstart.PID.log中获得跟踪日志,该日志显示已执行的每个命令的seconds.nanoseconds时间戳。从一个时间到下一个时间的差异是干预步骤所花费的时间。

在缩小范围时,您可以稍后移动set -x并提前set +x移动(或选择性地包含几个感兴趣的部分)。

虽然它不像GNU date的纳秒级那样精细,但Bash 5包含一个变量,它以微秒为单位给出时间。使用它可以避免为每个行生成外部可执行文件,并且可以在Mac或其他没有GNU date的地方工作 - 当然,只要你有Bash 5。更改PS4的设置:

PS4='+ $EPOCHREALTIME\011 '

正如@pawamoy所指出的,如果你有Bash 4.1或更高版本,你可以使用BASH_XTRACEFD将跟踪的输出发送到单独的文件描述符。来自this answer

#!/bin/bash

exec 5> command.txt
BASH_XTRACEFD="5"

echo -n "hello "

set -x
echo -n world
set +x

echo "!"

这将导致跟踪输出转到文件command.txt,使stdoutstdout正常输出(或单独重定向)。

答案 1 :(得分:95)

分析(4个答案)

修改日期:2016年3月添加 script 方法

阅读本文并因为分析是一个重要的步骤,我已经对整个SO问题进行了一些测试和研究,并且已经发布了答案。

有4个以上的答案:

  • 第一个是基于@DennisWilliamson的想法,但资源消耗较少
  • 第二个是我自己的(在此之前;)
  • 第三个是基于@fgm答案,但更准确。
  • 上次使用scriptscriptreplay计时文件

  • 最后,最后对表演进行一点比较。

使用set -xdate,但使用有限的分叉

从@ DennisWilliamson的想法中获取,但是使用以下语法,只有一个初始fork到3个命令:

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>&-

通过运行此脚本,您可以创建2个文件:/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

最近的 GNU / Linux内核上使用trap debug/proc/timer_list没有 分支

GNU / Linux 的最新内核中,您可能会找到一个名为/proc的{​​{1}}文件:

timer_list

其中当前时间是grep 'now at\|offset' /proc/timer_list now at 5461935212966259 nsecs .offset: 0 nsecs .offset: 1383718821564493249 nsecs .offset: 0 nsecs 的总和,但以纳秒为单位。

因此,对于计算经过时间,不需要知道偏移量。

对于这类工作,我写了elap.bash (V2),来自以下语法:

5461935212966259 + 1383718821564493249

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可以完成这项工作:

strace

但是可能会有很多东西!

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

原始的 bash脚本并不容易理解......

使用{ 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) --- script计时文件

作为 BSD Utils 的一部分,scriptreplay(和script)是一个非常古老的工具,可用于配置bash,占用空间非常小。

scriptreplay

将产生:

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

测试和结论

要进行测试,我已经在bash complex hello world下载了第二个示例,此脚本大约需要0.72秒才能在我的主机上完成。

我已添加以下脚本之一:

  • 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 函数

    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
  • #!/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

时报

比较执行时间(在我的主机上):

  • 直接 0.72秒
  • elap.bash 13.18秒
  • 设置+日期@ PS4 54.61秒
  • 设置+ 1分叉 1.45秒
  • 脚本和计时文件 2.19秒
  • strace 4.47秒

输出

  • script -t helloworld.log 2>helloworld.tim -c ' bash -x complex_helloworld-2.sh' >/dev/null 函数

    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示例脚本)

    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

结论

嘛!如果我的纯bash 在每个命令上分叉更快,我的纯bash意味着对每个命令进行一些操作。

专门用于记录和存储的独立过程的方式显然更有效。

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 是一种有趣的方式,更详细,但难以阅读。

stracescript和加速因子非常好,精度不一样,这是基于控制台交换而不是进程 执行,但非常轻松和有效(不同目标,不同用途)。

最后,我认为更高效,可读性和性能是 scriptreplay ,这是第一个答案,但很好,根据具体情况,我会使用某些时间{{ 1}}和/或set + 1 fork

答案 2 :(得分:18)

跟踪系统调用通常很有帮助

strace -c -f ./script.sh

从手册:

-c为每个系统调用计算时间,调用和错误,并在程序退出时报告摘要。

-f跟踪子进程...

这不完全是您想要的,以及面向行的探查器会向您显示的内容,但它通常有助于找到热点。

答案 3 :(得分:5)

您可以使用 DEBUG 条件查看trap命令。有一种方法可以设置要与命令一起执行的命令。请参阅答案的注释。

答案 4 :(得分:1)

在脚本前面添加它:

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

答案 5 :(得分:0)

时间,xtrace,bash -x,set -xset+xhttp://tldp.org/LDP/Bash-Beginners-Guide/html/sect_02_03.html)仍然是调试脚本的正统方式。

无论如何扩大我们的视野,都可以对某些系统进行检查,以便对通常的Linux程序[here one of the lists]进行调试和分析,例如它应该是基于valgrind的有用的,特别是调试内存或sysprof来分析整个系统:

对于sysprof:

  

使用sysprof,您可以分析计算机上运行的所有应用程序,包括多线程或多处理应用程序......

然后选择你感兴趣的子流程分支。


对于Valgrind:
通过一些更多的健身房,似乎可以向Valgrind提供可见一些我们通常从二进制安装的程序(例如OpenOffice)。

如果明确要求,可以从FAQ of valgrind中读取Valgrind将对子进程进行概要分析。

  

...即使默认情况下,它的配置文件只跟踪顶级进程,   因此,如果您的程序是由shell脚本,Perl脚本或类似的东西启动的,Valgrind将跟踪shell,Perl解释器或等效的...

启用此选项后会执行此操作

 --trace-children=yes 

其他参考文献:

答案 6 :(得分:0)

Alan Hargreaves post描述了使用DTrace提供程序分析Bourne shell脚本的方法。据我所知,这适用于Solaris和OpenSolaris(参见:/bin/sh DTrace Provider)。

因此根据sh_flowtime.d给出以下dtrace脚本(GH original):

#!/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命令,您可以对输出进行排序以显示最耗电的呼叫。

我不知道任何提供者探测器可用于其他shell,所以做一些研究(GitHub搜索?)或者如果你想投入一些时间,你可以根据现有的 sh <编写这样的/ em>示例:(见:How to activate sh DTrace Provider?)。