Як профіль повільного запуску сценарію bash shell?


124

Моя баш-шкаралупа займає до 3-4 секунд для запуску, тоді як якщо я запускаю її, --norcвона працює негайно.

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

Як я можу профілювати свої скрипти bash і бачити, які команди потребують більшої кількості часу для запуску?


3
Я профілював сценарії, і більшу частину часу витрачав під час налаштування bash_completion.
Андреа Спадаччіні

1
Це не дивно, оскільки це досить велико. Ви можете прискорити це, видаливши деталі, про які ви знаєте, що вам ніколи не знадобляться, якщо ви хочете перейти до проблем із збереженням змін через оновлення тощо.
Призупинено до подальшого повідомлення.

2
Можна порівняти: time bash -c 'exit'і time bash -i -c 'exit'та може грати з --norcі --noprofile.
Ф. Хаурі

Дивіться також цю відповідь (відмова: це моє). Не зовсім те, про що ви просите, але, безумовно, пов’язане: unix.stackexchange.com/a/555510/384864
Йохан Уоллес

Відповіді:


128

Якщо у вас є 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який показує часову позначку секунди.наносекунд кожної команди, яка виконувалася. Різниця від часу до часу - це кількість часу, яке пройшло втручається.

Коли ви звужуєте речі, ви можете переміщатись set -xпізніше і set +xраніше (або виділити декілька розділів, що цікавлять, вибірково).

Хоча це не так дрібнозернисто, як dateнаносекунди GNU , Bash 5 включає змінну, яка дає час у мікросекундах. Використовуючи це, ви позбавите від нересту зовнішнього виконуваного файлу для кожного рядка та роботи на Macs або інших місцях, де немає GNU date- якщо ви, звичайно, маєте Bash 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бути вихід нормально (або бути перенаправлено окремо).


Це нормально, що запит оболонки невидимий і що мої команди не відгукуються назад? Однак я отримав слід, щоб я міг розпочати аналіз .. велике спасибі!
Андреа Спадаччини

1
@AndreaSpadaccini: Фінал execповинен повернути fd2 до нормального, щоб ви отримали швидке повернення.
Призупинено до подальшого повідомлення.

7
... насправді, з bash 4.2, можна зробити краще - використання \D{...}в PS4дозволяє розширювати повністю довільні рядки формату часу без накладних витрат запуску dateяк підпроцесу.
Чарльз Даффі

3
@CharlesDuffy: Це обоє дуже здорово. Однак GNU dateрозуміє, %Nі Bash 4.2 не робить (бо strftime(3)не працює) в системі GNU - так довільно з обмеженнями. Ваша думка щодо продуктивності та роздільної здатності є хорошою, і користувач повинен робити вибір з розумом, пам’ятаючи, що показник ефективності є тимчасовим, лише під час налагодження (і лише тоді, коли set -xвін діє).
Призупинено до подальшого повідомлення.

1
З Bash 4 можна також використовувати змінну BASH_XTRACEFD для перенаправлення виводу налагодження на інший дескриптор файлу, ніж один за замовчуванням (2 або stderr). Це дуже допомагає, коли настає час аналізу результатів (даних профілювання), оскільки не потрібно більше розплутувати stderr і встановлювати -x вихід (так багато крайових випадків).
pawamoy

107

Профілювання (4 відповіді)

Редагувати: березня 2016 рокуscript метод додавання

Читаючи це, і тому, що профілювання є важливим кроком, я зробив кілька тестів та досліджень щодо всього цього питання ТА і вже розмістив відповіді.

Є відповідь 4+:

  • Перша заснована на ідеї @ DennisWilliamson, але із значно меншим споживанням ресурсів
  • Другий був мій власний (до цього;)
  • Третій заснований на @fgm відповіді, але більш точний.
  • Останнє використання script, scriptreplayі файл синхронізації .

  • Нарешті, невелике порівняння виступів наприкінці.

Використання set -xта dateз обмеженими виделками

Візьміть із ідеї @ DennisWilliamson, але з наступним синтаксисом буде лише одна початкова вилка на 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 - це ідентифікатор процесу запущеного сценарію).

Ви можете їх представити, використовуючи 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

(Див. Коментарі для повного синтаксису)

Таким чином, ви можете просто додати цей рядок у верхній частині сценарію:

. 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

Читати їх буде трохи складніше:

{
    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і файл синхронізації

Як частина BSD Utils , scriptscriptreplay) - це дуже старий інструмент, який можна використовувати для профілювання баш, з дуже маленьким слідом.

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

Тести та висновок

Щоб зробити тести, я завантажив другий зразок у сложному світі привіт , цей сценарій потребує приблизно 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і початковою форкою для тривалої команди 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 сек
  • елап.баш 13,18 сек
  • встановити + дата @ PS4 54,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 швидше, ніж запит на дату для кожної команди , мій чистий bash передбачає деякі операції над кожною командою.

Спосіб виділення незалежного процесу реєстрації та зберігання явно є більш ефективним.

strace цікавий спосіб, більш детальний, але важкий для читання.

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

Нарешті, я вважаю, що ефективніше в читанні та виконанні set + 1 fork. Перша відповідь, але добре, залежно від конкретного випадку, я використовую колись straceі / або scriptзанадто.



2
Розділ " Таймс " є досить інформативним і доводить додому, що виделки не мають чим чхати (справді повністю домінують у багатьох видах сценаріїв). +1 за хорошу (якщо давно) відповідь. Можливо , в майбутньому ви повинні розглянути питання розміщення окремих відповідей
sehe

1
Велике спасибі, @sehe! Ви знайдете повний готовий для запуску вихідний файл bash: elap-bash-v3 (з деякими функціями, такими як, що дозволяють прозоро використовувати STDIN та STDERR )
F. Hauri

1
У останніх версіях bash (> = 4.1) ви можете зробити exec {BASH_XTRACEFD}>замість цього exec 3>&2 2>файл журналу заповнити лише результатами реєстрації трас, а не іншим результатом stderr.
ws_e_c421

1
Виконання методу обробки однієї дати є дуже розумним, і я віддаю перевагу точності, що стосується другої секунди. Бо script.shя можу просто робити bash -c "exec {BASH_XTRACEFD}> >(tee trace.log | sed -u 's/^.*$//' | date -f - +%s.%N > timing.log); set -x; . script.shта отримувати дані профілювання без змін script.sh. Коли точність другої секунди не потрібна, мені подобається, bash -c "exec {BASH_XTRACEFD}>trace.log; set -x; PS4='+\t'; . script.shякий час друкує кожну лінію сліду з другою точністю і без прискорення на сьогоднішній день (низький накладний рівень).
ws_e_c421

17

Це часто допомагає простежити системні виклики

strace -c -f ./script.sh

З посібника:

-c Порахуйте час, дзвінки та помилки для кожного системного виклику та повідомте підсумки про вихід програми.

-як слідкуйте за процесами дитини ...

Це не саме те, що ви хочете, і те, що показував би лінійний профілер, але це зазвичай допомагає знайти гарячі точки.


5

Можливо, ви подивитеся на trapкоманду з умовою DEBUG . Існує спосіб встановити команду (команду), яку слід виконувати разом з вашими командами. Дивіться примітки до відповіді.


@ Деніс Вільямсон: Я не користувався цим деякий час, але в довідці в моїй системі зазначено, що "Якщо SIGNAL_SPEC DEBUG, ARG виконується після кожної простої команди".

Від Bash 4.0.33 help trap: "Якщо SIGNAL_SPEC є DEBUG, ARG виконується перед кожною простою командою." У Bash 3.2 написано "після". Це друкарня. Станом на Bash 2.05b, він запускався раніше. Довідка : "У цьому документі детально описані зміни між цією версією, bash-2.05b-alpha1 та попередньою версією, bash-2.05a-release. ... 3. Нові функції в Bash ... w. СЛУЖАЛЬНА пастка зараз запустіть перед простими командами, ((...)) командами, [[...]] умовними командами та для ((...)) циклів. " Тестування в кожній версії підтверджує, що це було раніше .
Призупинено до подальшого повідомлення.

@Денніс Вільямсон: Гаразд, тоді я маю версію. Я виправляю відповідь :)

0

Час, xtrace, bash -x set -xта set+x( http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_02_03.html ) залишаються ортодоксальним способом налагодження сценарію.

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

Для sysprof:

За допомогою sysprof ви можете профілювати всі програми, що працюють на вашій машині, включаючи багатопотокові або багатопроцесорні програми ...

А потім виберіть галузь підпроцесів, які вам будуть цікаві.


Для Valgrind:
З деякою більшою кількістю тренажерних залів, здається, можна зробити Valgrind видимими деякі програми, які ми зазвичай встановлюємо з двійкових (наприклад, OpenOffice ).

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

... Навіть якщо він за замовчуванням у профілі лише відстежує процес верхнього рівня, і тому, якщо вашу програму запускає скрипт оболонки, скрипт Perl або щось подібне, Valgrind відстежить оболонку, або інтерпретатор Perl, або аналог. ..

Це робитиметься з увімкненою цією опцією

 --trace-children=yes 

Додаткові довідки:

  • Посібник Valgrind .
  • Деякі новини про інтерфейси KCachegrind та Callgrind або також тут , як і раніше, використовуються як повідомляється з вікі CERN
  • gdb посібник. для gdb, що може стати корисною для програми c, c ++, викликаної сценарієм.

1
Не суттєвий результат, але більшість цих порад, хоч і круті, тут не дуже актуальні. Якщо відповісти на відповідне запитання і відповісти самовідповідачем, то тут більш вітається - Google "стакковерф самостійно відповідає" на відповідний етикет.
Blaisorblade

0

Це повідомлення від Alan Hargreaves описує метод профілювання Bourne сценарію оболонки постачальника DTrace. Наскільки мені відомо, це працює з Solaris та OpenSolaris (див. / Bin / sh DTrace 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 шукати?) , Або якщо ви хочете витратити деякий час, ви можете написати , наприклад на основі існуючого ш наприклад: (див: Як активувати ш Провайдер DTrace? ).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.