web-dev-qa-db-fra.com

Comment profiler un démarrage bas du script Shell bash?

Mon shell bash prend jusqu'à 3-4 secondes pour démarrer, tandis que si je le démarre avec --norc il s'exécute immédiatement.

J'ai commencé le "profilage" /etc/bash.bashrc et ~/.bashrc en insérant manuellement des instructions return et en recherchant des améliorations de vitesse, mais ce n'est pas un processus quantitatif et il n'est pas efficace.

Comment puis-je profiler mes scripts bash et voir quelles commandes prennent le plus de temps à démarrer?

114
Andrea Spadaccini

Si vous avez GNU date (ou une autre version pouvant produire des nanosecondes), faites-le au début de /etc/bash.bashrc (ou partout où vous souhaitez commencer une trace dans n'importe quel script Bash):

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

ajouter

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

au bout du ~/.bashrc (ou à la fin de la section de tout script Bash dont vous souhaitez arrêter le suivi). Le \011 est un caractère de tabulation octal.

Vous devriez obtenir un journal de suivi dans /tmp/bashstart.PID.log qui affiche l'horodatage en secondes. nanosecondes de chaque commande exécutée. La différence d'une fois à l'autre est la durée de l'étape intermédiaire.

Lorsque vous réduisez les choses, vous pouvez déplacer set -x plus tard et set +x plus tôt (ou mettez plusieurs sections d'intérêt entre parenthèses de manière sélective).

Bien qu'il ne soit pas aussi fin que les nanosecondes de GNU date, Bash 5 inclut une variable qui donne l'heure en microsecondes. Son utilisation vous évite de générer un exécutable externe pour chaque ligne et fonctionne sur Mac ou ailleurs qui n'a pas GNU date - tant que vous avez Bash 5, bien sûr. Modifiez le paramètre de PS4:

PS4='+ $EPOCHREALTIME\011 '

Comme l'a souligné @pawamoy, vous pouvez utiliser BASH_XTRACEFD pour envoyer la sortie de la trace vers un descripteur de fichier distinct si vous disposez de Bash 4.1 ou version ultérieure. De cette réponse :

#!/bin/bash

exec 5> command.txt
BASH_XTRACEFD="5"

echo -n "hello "

set -x
echo -n world
set +x

echo "!"

Cela entraînera la sortie de trace pour aller dans le fichier command.txt laissant stdout et stdout pour être sortis normalement (ou être redirigés séparément).

118
Dennis Williamson

Profilage bash (4 réponses)

Edit: mars 2016 add script method

En lisant ceci et parce que profilage est une étape importante, j'ai fait quelques tests et recherches sur cet ensemble SO question et réponses déjà publiées).

Il y a 4+ réponses:

  • Le premier est basé sur l'idée de @ DennisWilliamson mais avec beaucoup moins de consommation de ressources
  • Le second était le mien (avant cela;)
  • Le troisième est basé sur la réponse @fgm, mais plus précis.
  • La dernière utilisation script, scriptreplay et fichier de synchronisation.

  • Enfin, une petite comparaison des performances à la fin.

En utilisant set -x et date mais avec un nombre limité fourches

Prenez de l'idée de @ DennisWilliamson, mais avec la syntaxe suivante, il n'y aura qu'un seul fork initial pour 3 commandes:

exec 3>&2 2> >(tee /tmp/sample-time.$$.log |
                 sed -u 's/^.*$/now/' |
                 date -f - +%s.%N >/tmp/sample-time.$$.tim)
set -x

Cette opération exécutera date une seule fois. Il y a une démo/test rapide pour montrer comment cela fonctionne:

for i in {1..4};do echo now;sleep .05;done| date -f - +%N

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

En exécutant ce script, vous créez 2 fichiers: /tmp/sample-XXXX.log et /tmp/sample-XXXX.tim (où XXXX est l'ID de processus d'exécution du script).

Vous pouvez les présenter en utilisant paste:

paste tmp/sample-XXXX.{tim,log}

Ou vous pouvez même calculer le temps de diff:

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

ou sur deux colonnes:

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

Peut rendre:

 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

En utilisant trap debug et /proc/timer_list on récent noyaux GNU/Linux, sans fourches.

Sous GNU/Linux les noyaux récents, vous pouvez trouver un /proc fichier nommé timer_list:

grep 'now at\|offset' /proc/timer_list
now at 5461935212966259 nsecs
  .offset:     0 nsecs
  .offset:     1383718821564493249 nsecs
  .offset:     0 nsecs

Où l'heure actuelle est la somme de 5461935212966259 + 1383718821564493249, mais en nanosecondes.

Donc pour calculer temps écoulé, il n'est pas nécessaire de connaître le décalage.

Pour ce type de travaux, j'ai écrit elap.bash (V2) , qui provient de la syntaxe suivante:

source elap.bash-v2

ou

. elap.bash-v2 init

(Voir les commentaires pour la syntaxe complète)

Vous pouvez donc simplement ajouter cette ligne en haut de votre script:

. elap.bash-v2 trap2

Petit échantillon:

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

Faites le rendu sur mon hôte:

 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

En utilisant trap2 au lieu de trap comme argument de la commande source:

#!/bin/bash

. elap.bash-v2 trap2
...

Rendra deux colonnes dernière commande et total:

 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

Utilisation de strace

Oui, strace pourrait faire le travail:

strace -q -f -s 10 -ttt sample-script 2>sample-script-strace.log

Mais il pourrait y avoir beaucoup de choses!

wc sample-script-strace.log
    6925  57637 586518 sample-script-strace.log

Utilisation d'une commande plus restreinte:

strace -f -s 10 -ttt -eopen,access,read,write ./sample-script 2>sample-script-strace.log

Videra le journal plus léger:

  4519  36695 374453 sample-script-strace.log

Selon ce que vous recherchez, vous pouvez être plus restrictif:

 strace -f -s 10 -ttt -eaccess,open ./sample-script 2>&1 | wc
  189    1451   13682

Leur lecture sera un peu plus difficile:

{
    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) ---

L'original script bash n'est pas si facile à suivre dans ce ...

Utilisation de script, scriptreplay et fichier de synchronisation

Dans le cadre de BSD Utils, script (et scriptreplay) est un très ancien outil qui peut être utilisé pour profiler bash, avec une très petite empreinte.

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
"'

Produira:

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

et générer deux fichiers:

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

Fichier script.log contient toutes les traces et script.tim est le fichier de synchronisation:

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

Vous pouvez voir l'exécution totale du temps avec les première et dernière lignes du fichier journal et/ou en résumant les temps dans le fichier de synchronisation:

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

Dans le fichier de synchronisation, la deuxième valeur est le nombre d'octets suivants dans le fichier journal correspondant. Cela vous laisse la possibilité de rejouer fichier journal avec éventuellement un facteur d'accélération:

scriptreplay script.{tim,log}

ou

scriptreplay script.{tim,log} 5

ou

 scriptreplay script.{tim,log} .2

Afficher les heures et les commandes côte à côte est également un peu plus complexe:

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

Tests et conclusion

Pour faire des tests, j'ai téléchargé le deuxième échantillon sur bash complex hello world , ce script prend environ 0,72 s pour terminer sur mon hôte.

J'ai ajouté en haut du script l'un des:

  • par elap.bash une fonction

    #!/bin/bash
    
    source elap.bash-v2 trap2
    
    eval "BUNCHS=(" $(Perl <<EOF | gunzip
    ...
    
  • par set -x et PS4

    #!/bin/bash
    
    PS4='+ $(date "+%s.%N")\011 '
    exec 3>&2 2>/tmp/bashstart.$$.log
    set -x
    
    eval "BUNCHS=(" $(Perl <<EOF | gunzip
    ...
    
  • par set -x et initialisation de la fourche à la longue commande 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
    
  • par script (et set +x)

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

Fois

Et comparez les temps d'exécution (sur mon hôte):

  • Direct 0,72 s
  • elap.bash 13,18 s
  • set + date @ PS4 54,61 sec
  • set + 1 fourchette 1,45 sec
  • script et fichier de synchronisation 2.19 sec
  • strace 4,47 sec

Les sorties

  • par elap.bash une fonction

         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))
    
  • par set -x et 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=
    
  • par set -x et initial fork à la longue commande exec (et mon deuxième paste exemple de 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=
    
  • par 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)         = ?
    
  • par 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
    

Conclusion

Bien! Si mon pure bash est plus rapide que forking to date on each command, my pure bash implique quelques opérations sur chaque commande.

La manière de dédier un processus indépendant de journalisation et de stockage est clairement plus efficace.

strace est un moyen intéressant, plus détaillé, mais difficile à lire.

script, avec scriptreplay et le facteur d'accélération est très bien aussi, pas la même précision car elle est basée sur l'échange de console au lieu de l'exécution du processus, mais très légère et efficace (pas le même objectif, pas la même utilisation ).

Enfin, je pense que le plus efficace, en termes de lisibilité et de performances est set + 1 fork, La première de cette réponse, mais en fin de compte, selon le cas spécifique, j'utilise parfois strace et/ou script aussi.

99
F. Hauri

Il aide souvent à tracer les appels système

strace -c -f ./script.sh

Du manuel:

-c Comptez le temps, les appels et les erreurs pour chaque appel système et signalez un résumé à la sortie du programme.

-f Tracer les processus enfants ...

Ce n'est pas exactement ce que vous voulez et ce qu'un profileur orienté ligne vous montrerait, mais cela aide généralement à trouver des points chauds.

18
Fritz G. Mehner

Vous pouvez jeter un œil à la commande trap avec la condition DEBUG . Il existe un moyen de définir une ou plusieurs commandes à exécuter avec vos commandes. Voir les notes de la réponse.

5
user332325

Cette publication par Alan Hargreaves décrit la méthode de profilage du script Bourne Shell à l'aide du fournisseur DTrace. Pour autant que je sache, cela fonctionne avec Solaris et OpenSolaris (voir: / bin/sh DTrace Provider ).

Donc, étant donné le script dtrace suivant ( sh_flowtime.d à GH sur la base de 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));
}

vous pouvez suivre le flux de fonctions, y compris les temps delta.

Exemple de sortie:

# ./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

Puis en utilisant sort -nrk7, vous pouvez trier la sortie pour afficher les appels les plus consommateurs.

Je ne connais pas de sondes de fournisseur disponibles pour d'autres shells, alors faites des recherches (recherche GitHub?) Ou si vous voulez investir un peu de temps, vous pouvez écrire celles-ci en fonction du sh existant sh exemple: (voir: Comment activer sh DTrace Provider? ).

0
kenorb

Heure, xtrace, bash -x, set -x et set+x ( http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_02_03.html ) reste la façon orthodoxe de déboguer un script.

Néanmoins, pour élargir notre horizon, il est possible de vérifier un système de débogage et de profilage disponible pour les programmes Linux habituels [ici une des listes] , par ex. il devrait en résulter une base utile basée sur valgrind spécialement pour déboguer la mémoire ou sysprof pour profiler l'ensemble du système:

Pour sysprof:

Avec sysprof, vous pouvez profiler toutes les applications qui s'exécutent sur votre machine, y compris une application multithread ou multiprocessed ...

Et après pour sélectionner la branche de sous-processus que vous trouvez intéressante.


Pour Valgrind:
Avec un peu plus de gym, il semble qu'il soit possible de rendre visible à Valgrind certains programmes que nous installons habituellement à partir de binaires (par exemple OpenOffice ).

Il est possible de lire dans FAQ de valgrind que Valgrind profilera les processus enfants si cela est explicitement demandé.

... Même si par défaut, il profile uniquement le processus de niveau supérieur, et donc si votre programme est démarré par un script Shell , un script Perl, ou quelque chose de similaire, Valgrind tracera le Shell, ou l'interpréteur Perl, ou équivalent ...

Il le fera avec cette option activée

 --trace-children=yes 

Références supplémentaires:

0
Hastur