Как профилировать bash shell script медленный запуск?

Для запуска моей оболочки bash требуется 3-4 секунды, а если я начинаю ее с --norc, она запускается немедленно.

Я начал "профилировать" /etc/bash.bashrc и ~/.bashrc, вручную вставив инструкции return и добиваясь повышения скорости, но это не количественный процесс, и он неэффективен.

Как я могу профилировать мои сценарии bash и посмотреть, какие команды занимают больше всего времени для запуска?

Ответ 1

Если у вас есть GNU date (или другая версия, которая может выводить наносекунды), сделайте это в начале /etc/bash.bashrc (или там, где вы хотите начать трассировку в любом Bash script):

PS4='+ $(date "+%s.%N")\011 '
exec 3>&2 2>/tmp/bashstart.$$.log
set -x

добавить

set +x
exec 2>&3 3>&-

в конце ~/.bashrc (или в конце раздела любого Bash script вы хотите, чтобы трассировка остановилась).

Вы должны получить журнал трассировки в /tmp/bashstart.PID.log, который показывает временную метку seconds.nanoseconds для каждой команды, которая была выполнена. Разница от одного времени к следующему - это время, в которое прошел промежуточный шаг.

По мере того как вы сужаете вещи, вы можете переместить set -x позже и set +x раньше (или выборочно скопировать несколько разделов интереса).

Ответ 2

Профилирование (4 ответа)

Изменить: март 2016 добавить script метод

Чтение этого и потому, что профилирование является важным шагом, я провел несколько испытаний и исследований по всему этому вопросу SO и уже опубликовал ответы.

Ответ на 4+:

  • Первый основан на идее @DennisWilliamson, но с гораздо меньшим потреблением ресурсов
  • Вторая была моей (до этого;)
  • Третий основан на ответе @fgm, но более точном.
  • Последнее использование script, scripreplay и файл синхронизации.

  • Наконец, небольшое сравнение выступлений в конце.

Использование set -x и date, но с ограниченными вилками

Возьмите из идеи @DennisWilliamson, но со следующим синтаксисом будет одна начальная fork для трех команд:

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

Пример script:

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

Запустив этот script, вы создадите 2 файла: /tmp/sample-XXXX.log и /tmp/sample-XXXX.tim (где XXXX - это идентификатор процесса запуска script).

Вы можете представить их с помощью 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/Linux, без.

В последних ядрах GNU/Linux вы можете найти файл /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

(См. комментарии для полного синтаксиса)

Итак, вы можете просто добавить эту строку вверху своего script:

. 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

Будет выгружать журнал ligther:

  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 не так просто в этом...

Используя script, scriptreplay и файл синхронизации

Как часть BSD Utils, scriptscriptreplay) - очень старый инструмент, который можно использовать для профиля 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

Тесты и выводы

Чтобы выполнить тесты, я загрузил образец второй в bash комплексный мир приветствия, этот script занимает приблизительно 0,72 секунды для завершения моего хоста.

Я добавлю в верхнюю часть script одну из следующих:

  • функцией 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 и начальный fork для команды 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
    
  • scriptset +x)

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

Времена

И сравните время выполнения (на моем хосте):

  • Прямой 0,72 с
  • elap.bash 13.18 сек
  • set + date @PS4 54.61 сек
  • set + 1 fork 1,45 с
  • script и файл синхронизации 2,19 с
  • strace 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 и начальный fork для команды long exec (и мой второй paste образец script)

     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 на сегодняшний день для каждой команды, мой чистый bash подразумевает некоторые операции над каждой командой.

Способ выделения независимого процесса для регистрации и хранения явно более эффективен.

strace интересный, более подробный, но трудный для чтения.

script, с scriptreplay и коэффициент ускорения тоже очень хорош, не такая же точность, как и на основе обмена консолями вместо процесса исполнение, но очень легкое и эффективное (не одна и та же цель, не такое же использование).

Наконец, я считаю, что более эффективная, читабельность и производительность set + 1 fork. Первый из этих ответов, но в порядке, в зависимости от конкретного случая, я иногда использую strace и/или screen.

Ответ 3

Он часто помогает отслеживать системные вызовы

strace -c -f ./script.sh

Из руководства:

-c Считать время, вызовы и ошибки для каждого системного вызова и сообщить о сводке о выходе программы.

-f Трассировать дочерние процессы...

Это не совсем то, что вы хотите, и то, что будет показывать вам профилировщик, ориентированный на линию, но обычно помогает найти горячие точки.

Ответ 4

Вы можете взглянуть на команду trap с условием DEBUG. Существует способ установить команды, которые будут выполняться вместе с вашими командами. См. Примечания к ответу.

Ответ 5

Этот post Алан Харгривз описывает метод профилирования оболочки Bourne script с помощью поставщика DTrace. Насколько я знаю, это работает с Solaris и OpenSolaris (см. /bin/sh DTrace Provider).

Итак, задайте следующий dtrace script (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 примера: (см.: Как активировать sh DTrace Provider?).

Ответ 6

Время, xtrace, bash -x, set -x и set+x (http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_02_03.html) остаются ортодоксальным способом отладки a script.

Бесшумный, чтобы увеличить наш горизонт, можно дать чек какой-то системе для отладки и профилирования, доступной для обычных программ Linux [здесь один из списков], например это должно привести к полезным, основанному на valgrind, особенно для отладки памяти или sysprof, чтобы просмотреть всю систему:

Для sysprof:

С помощью sysprof вы можете профилировать все приложения, запущенные на вашем компьютере, включая многопоточное или многопроцессорное приложение...

И после того, как выбрать ветвь подпроцессов, которые вам интересны.


Для Вальгринда:
С некоторыми более тренажерными залами, кажется, можно сделать видимыми для Valgrind некоторые программы, которые мы обычно устанавливаем из двоичных (например, OpenOffice).

Можно читать из FAQ valgrind, что Valgrind будет профилировать дочерние процессы, если их явно запрашивают.

... Даже если по умолчанию профили только отслеживают процесс верхнего уровня, и, если ваша программа запускается оболочкой script, Perl script или что-то подобное, Valgrind будет трассировать оболочку или интерпретатор Perl или эквивалент...

Он будет делать это с включенной опцией

 --trace-children=yes 

Дополнительные ссылки:

  • Valgrind manual.
  • Некоторые новости об интерфейсах KCachegrind и Callgrind или также здесь, все еще используется, как сообщается из wiki CERN
  • gdb руководство. для gdb, которое может результат полезен для профиля c, С++-программы, вызываемой script.