Làm thế nào để cấu hình một tập lệnh bash shell khởi động chậm?


124

Shell bash của tôi mất tới 3-4 giây để khởi động, trong khi nếu tôi khởi động nó --norcthì nó sẽ chạy ngay lập tức.

Tôi bắt đầu "định hình" /etc/bash.bashrc~/.bashrcbằng cách chèn thủ công các returncâu lệnh và tìm cách cải thiện tốc độ, nhưng nó không phải là một quá trình định lượng và nó không hiệu quả.

Làm cách nào tôi có thể cấu hình các tập lệnh bash của mình và xem các lệnh nào mất nhiều thời gian nhất để khởi động?


3
Tôi đã lược tả các tập lệnh và phần lớn thời gian được dành cho quá trình thiết lập bash_completion.
Andrea Spadaccini

1
Điều đó không đáng ngạc nhiên vì nó khá lớn. Bạn có thể tăng tốc độ bằng cách loại bỏ các phần bạn biết bạn sẽ không bao giờ cần nếu bạn muốn gặp rắc rối trong việc duy trì các thay đổi của mình qua các bản cập nhật, v.v.
Tạm dừng cho đến khi có thông báo mới.

2
Bạn có thể so sánh: time bash -c 'exit'time bash -i -c 'exit'và có thể chơi với --norc--noprofile.
F. Hauri

Xem thêm câu trả lời này (từ chối trách nhiệm: nó là của tôi). Không chính xác những gì bạn đang hỏi, nhưng chắc chắn có liên quan: unix.stackexchange.com/a/555510/384864
Johan Walles

Câu trả lời:


128

Nếu bạn có GNU date(hoặc phiên bản khác có thể xuất ra nano giây), hãy thực hiện việc này ở đầu /etc/bash.bashrc(hoặc bất cứ nơi nào bạn muốn bắt đầu theo dõi trong bất kỳ tập lệnh Bash nào):

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

thêm vào

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

ở cuối ~/.bashrc(hoặc ở cuối phần của bất kỳ tập lệnh Bash nào bạn muốn theo dõi để dừng lại). Đây \011là một nhân vật tab bát phân.

Bạn sẽ nhận được một nhật ký theo dõi trong /tmp/bashstart.PID.logđó hiển thị dấu thời gian giây.nanoseconds của mỗi lệnh đã được thực thi. Sự khác biệt từ lần này đến lần khác là khoảng thời gian mà bước can thiệp đã thực hiện.

Khi bạn thu hẹp mọi thứ, bạn có thể di chuyển set -xmuộn hơn và set +xsớm hơn (hoặc đóng khung một số phần quan tâm có chọn lọc).

Mặc dù nó không được phân loại tốt như datenano giây của GNU , Bash 5 bao gồm một biến mang lại thời gian tính bằng micro giây. Sử dụng nó giúp bạn tránh sinh ra một tệp thực thi bên ngoài cho mọi dòng và hoạt động trên máy Mac hoặc các nơi khác không có GNU date- tất nhiên miễn là bạn có Bash 5, tất nhiên. Thay đổi cài đặt của PS4:

PS4='+ $EPOCHREALTIME\011 '

Như được chỉ ra bởi @pawamoy, bạn có thể sử dụng BASH_XTRACEFDđể gửi đầu ra của dấu vết đến một mô tả tệp riêng nếu bạn có Bash 4.1 trở lên. Từ câu trả lời này :

#!/bin/bash

exec 5> command.txt
BASH_XTRACEFD="5"

echo -n "hello "

set -x
echo -n world
set +x

echo "!"

Điều này sẽ khiến đầu ra theo dõi đi đến tệp command.txtrời đi stdoutstdoutđược xuất ra bình thường (hoặc được chuyển hướng riêng).


Có phải bình thường rằng dấu nhắc shell là vô hình và các lệnh của tôi không được lặp lại? Tuy nhiên, tôi đã có dấu vết để tôi có thể bắt đầu phân tích .. cảm ơn rất nhiều!
Andrea Spadaccini

1
@AndreaSpadaccini: Trận chung kết execsẽ đưa fd2 trở lại bình thường để bạn có thể nhận lại lời nhắc.
Tạm dừng cho đến khi có thông báo mới.

7
... thực sự, với bash 4.2, người ta có thể làm tốt hơn - sử dụng \D{...}trong PS4cho phép các chuỗi định dạng thời gian hoàn toàn tùy ý được mở rộng mà không cần chi phí hiệu năng của việc khởi chạy datenhư một quy trình con.
Charles Duffy

3
@CharlesDuffy: Cả hai đều thực sự tuyệt vời. Tuy nhiên GNU datehiểu %Nvà Bash 4.2 không (vì strftime(3)không) trên hệ thống GNU - do đó tùy ý với các giới hạn. Quan điểm của bạn về hiệu suất so với độ phân giải là một điểm tốt và người dùng nên đưa ra lựa chọn sáng suốt, hãy nhớ rằng hiệu năng đạt được chỉ là tạm thời trong quá trình gỡ lỗi (và chỉ khi set -xcó hiệu lực).
Tạm dừng cho đến khi có thông báo mới.

1
Với Bash 4, người ta cũng có thể sử dụng biến BASH_XTRACEFD để chuyển hướng đầu ra gỡ lỗi sang một bộ mô tả tệp khác so với biến mặc định (2 hoặc stderr). Nó giúp ích rất nhiều khi đến lúc phân tích đầu ra (dữ liệu lược tả), vì người ta không phải gỡ rối đầu ra stderr và set -x nữa (rất nhiều trường hợp cạnh).
pawamoy

107

Hồ sơ (4 câu trả lời)

Chỉnh sửa: Tháng 3 năm 2016 thêm scriptphương thức

Đọc điều này và bởi vì hồ sơ là một bước quan trọng, tôi đã thực hiện một số thử nghiệm và nghiên cứu về toàn bộ câu hỏi SO này và đã đăng câu trả lời.

Có hơn 4 câu trả lời:

  • Đầu tiên dựa trên ý tưởng của @ DennisWilliamson nhưng với mức tiêu thụ tài nguyên ít hơn nhiều
  • Thứ hai là của riêng tôi (trước đây;)
  • Thứ ba là dựa trên câu trả lời @fgm, nhưng chính xác hơn.
  • Việc sử dụng cuối cùng script, scriptreplaytập tin thời gian .

  • Cuối cùng, một chút so sánh các màn trình diễn ở cuối.

Sử dụng set -xdatenhưng với dĩa giới hạn

Lấy từ ý tưởng của @ DennisWilliamson, nhưng với cú pháp sau, sẽ chỉ có một ngã ba ban đầu cho 3 lệnh:

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

Làm điều này sẽ datechỉ chạy một lần. Có một bản demo / thử nghiệm nhanh để cho thấy nó hoạt động như thế nào:

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

Kịch bản mẫu:

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

Bằng cách chạy tập lệnh này, bạn tạo 2 tệp: /tmp/sample-XXXX.log/tmp/sample-XXXX.tim(trong đó XXXX là id quá trình chạy tập lệnh).

Bạn có thể trình bày chúng bằng cách sử dụng paste:

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

Hoặc thậm chí bạn có thể tính thời gian khác:

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

hoặc trên hai cột:

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

Có thể kết xuất:

 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

Sử dụng trap debug/proc/timer_listtrên các hạt nhân GNU / Linux gần đây , không có nhánh .

Trong các nhân gần đây của GNU / Linux , bạn có thể tìm thấy một /proctệp có tên timer_list:

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

Trong đó thời gian hiện tại là tổng của 5461935212966259 + 1383718821564493249, nhưng tính bằng nano giây.

Vì vậy, để tính toán thời gian trôi qua , không cần phải biết bù.

Đối với loại công việc này, tôi đã viết elap.bash (V2) , có nguồn gốc theo cú pháp sau:

source elap.bash-v2

hoặc là

. elap.bash-v2 init

(Xem bình luận cho cú pháp đầy đủ)

Vì vậy, bạn chỉ cần thêm dòng này vào đầu tập lệnh của mình:

. elap.bash-v2 trap2

Mẫu nhỏ:

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

Đừng kết xuất trên máy chủ của tôi:

 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

Sử dụng trap2thay vì traplàm đối số cho lệnh nguồn:

#!/bin/bash

. elap.bash-v2 trap2
...

Sẽ kết xuất hai cột lệnh cuối cùng và tổng :

 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

Sử dụng strace

Có, stracecó thể làm công việc:

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

Nhưng có thể làm rất nhiều thứ!

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

Sử dụng lệnh hạn chế hơn:

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

Sẽ đổ log nhẹ hơn:

  4519  36695 374453 sample-script-strace.log

Tùy thuộc vào những gì bạn đang tìm kiếm, bạn có thể hạn chế hơn:

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

Đọc chúng sẽ khó hơn một chút:

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

Kịch bản bash ban đầu không dễ theo dõi trong ...

Sử dụng script, scriptreplaytập tin thời gian

Là một phần của BSD Utils , script(và scriptreplay) là một công cụ rất cũ có thể được sử dụng để lập hồ sơ bash, với một dấu chân rất nhỏ.

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

Sẽ sản xuất:

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

và tạo hai tệp:

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

Tệp script.logchứa tất cả dấu vết và script.timtệp thời gian :

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

Bạn có thể thấy tổng thời gian thực hiện với dòng logfile đầu tiên và cuối cùng và / hoặc bằng cách tóm tắt thời gian trong tệp thời gian:

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

Trong tệp thời gian, giá trị thứ hai là số byte tiếp theo trong logfile tương ứng. Điều này cho phép bạn phát lại tùy chọn tệp nhật ký với hệ số tăng tốc :

scriptreplay script.{tim,log}

hoặc là

scriptreplay script.{tim,log} 5

hoặc là

 scriptreplay script.{tim,log} .2

Hiển thị thời gian và các lệnh cạnh nhau cũng phức tạp hơn một chút:

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

Kiểm tra và kết luận

Để thực hiện các bài kiểm tra, tôi đã tải xuống mẫu thứ hai tại bash hello world , tập lệnh này mất khoảng 0,72 giây để hoàn tất trên máy chủ của tôi.

Tôi đã thêm vào đầu tập lệnh một trong:

  • theo elap.bashchức năng

    #!/bin/bash
    
    source elap.bash-v2 trap2
    
    eval "BUNCHS=(" $(perl <<EOF | gunzip
    ...
  • bởi set -xPS4

    #!/bin/bash
    
    PS4='+ $(date "+%s.%N")\011 '
    exec 3>&2 2>/tmp/bashstart.$$.log
    set -x
    
    eval "BUNCHS=(" $(perl <<EOF | gunzip
    ...
  • bởi set -xngã ba ban đầu để lệnh exec dài

    #!/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
  • bởi script(và set +x)

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

Thời đại

Và so sánh thời gian thực hiện (trên máy chủ của tôi):

  • Trực tiếp 0,72 giây
  • elap.bash 13,18 giây
  • đặt + ngày @ PS4 54,61 giây
  • đặt + 1 ngã ba 1,45 giây
  • tập lệnh và tập tin thời gian 2,19 giây
  • bước đi 4,47 giây

Đầu ra

  • theo elap.bashchức năng

         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))
  • bởi set -xPS4

    ++ 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=
  • bởi set -xngã ba ban đầu để lệnh exec dài (và pastetập lệnh mẫu thứ hai của tôi )

     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=
  • bởi 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)         = ?
  • bởi 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

Phần kết luận

Tốt! Nếu bash thuần của tôi nhanh hơn so với việc cập nhật từng lệnh , thì bash thuần của tôi ngụ ý một số thao tác trên mỗi lệnh.

Cách dành một quy trình độc lập để ghi nhật ký và lưu trữ rõ ràng hiệu quả hơn.

strace là một cách thú vị, chi tiết hơn, nhưng khó đọc.

script, với scriptreplayvà hệ số tăng tốc cũng rất đẹp, không chính xác như điều này dựa trên trao đổi bàn điều khiển thay vì thực hiện quy trình, nhưng rất nhẹ và hiệu quả (không cùng mục tiêu, không sử dụng giống nhau).

Cuối cùng, tôi nghĩ rằng hiệu quả hơn, về khả năng đọc và hiệu suất là set + 1 fork, Đầu tiên của câu trả lời này, nhưng tốt, tùy thuộc vào trường hợp cụ thể, tôi sử dụng đôi khi stracevà / hoặc scriptquá.



2
Phần Times khá nhiều thông tin và lái xe về nhà rằng các dĩa không có gì để hắt hơi (thực sự hoàn toàn thống trị nhiều loại kịch bản). +1 cho câu trả lời hay (nếu được vẽ dài). Có lẽ trong tương lai bạn nên xem xét đăng câu trả lời riêng biệt
sehe

1
Rất cám ơn, @sehe! Bạn sẽ tìm thấy một tệp nguồn bash sẵn sàng để chạy đầy đủ ở đó: elap-bash-v3 (với một số tính năng như cho phép sử dụng STDIN STDERR trong suốt )
F. Hauri

1
Trên các phiên bản gần đây của bash (> = 4.1), bạn có thể thực hiện exec {BASH_XTRACEFD}>thay vì exec 3>&2 2>sẽ chỉ điền vào tệp nhật ký với đầu ra ghi nhật ký theo dõi chứ không phải đầu ra stderr khác.
ws_e_c421

1
Phương thức thực hiện cho một phương thức xử lý ngày duy nhất là rất thông minh và ưu tiên của tôi cho độ chính xác dưới giây. Đối với script.sh, tôi chỉ có thể làm bash -c "exec {BASH_XTRACEFD}> >(tee trace.log | sed -u 's/^.*$//' | date -f - +%s.%N > timing.log); set -x; . script.shvà nhận dữ liệu hồ sơ mà không cần sửa đổi script.sh. Khi không cần độ chính xác phụ thứ hai, tôi thích bash -c "exec {BASH_XTRACEFD}>trace.log; set -x; PS4='+\t'; . script.shthời gian nào đóng dấu mỗi dòng dấu vết với độ chính xác thứ hai và không cần cập nhật (chi phí thấp).
ws_e_c421

17

Nó thường giúp theo dõi các cuộc gọi hệ thống

strace -c -f ./script.sh

Từ hướng dẫn:

-c Đếm thời gian, cuộc gọi và lỗi cho mỗi cuộc gọi hệ thống và báo cáo tóm tắt về thoát chương trình.

-f Trace quy trình con ...

Đây không phải là chính xác những gì bạn muốn và những gì một trình lược tả định hướng dòng sẽ hiển thị cho bạn nhưng nó thường giúp tìm các điểm nóng.


5

Bạn có thể xem traplệnh với điều kiện DEBUG . Có một cách để đặt (các) lệnh được thực thi cùng với các lệnh của bạn. Xem ghi chú cho câu trả lời.


@Dennis Williamson: Tôi đã không sử dụng nó trong một thời gian, nhưng sự trợ giúp trên hệ thống của tôi nói rằng "Nếu một TÍN HIỆU là DEBUG, ARG được thực thi sau mỗi lệnh đơn giản."

Từ Bash 4.0.33 help trap: "Nếu một TÍN HIỆU là DEBUG, ARG được thực thi trước mỗi lệnh đơn giản." Trong Bash 3.2, nó nói "sau". Đó là một lỗi đánh máy. Kể từ Bash 2.05b, nó chạy trước. Tham khảo : "Tài liệu này nêu chi tiết các thay đổi giữa phiên bản này, bash-2.05b-alpha1 và phiên bản trước đó, bash-2.05a-phát hành. ... 3. Các tính năng mới trong Bash ... w. Bẫy DEBUG hiện đã có chạy trước các lệnh đơn giản, ((...)) lệnh, [[...]] lệnh có điều kiện và vòng lặp for ((...)). " Thử nghiệm trong mỗi phiên bản xác nhận rằng đó là trước đây .
Tạm dừng cho đến khi có thông báo mới.

@Dennis Williamson: Ok, đó là phiên bản tôi có. Tôi sửa câu trả lời :)

0

Thời gian, xtrace, bash -x set -xset+x( http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_02_03.html ) vẫn là cách chính thống để gỡ lỗi một tập lệnh.

Không bao giờ mở rộng tầm nhìn của chúng tôi, có thể kiểm tra một số hệ thống để gỡ lỗi và định hình có sẵn cho các chương trình Linux thông thường [ở đây là một trong các danh sách] , ví dụ: nó sẽ mang lại kết quả hữu ích dựa trên valgrind đặc biệt là gỡ lỗi bộ nhớ hoặc sysprof cho hồ sơ toàn bộ hệ thống:

Đối với hệ thống:

Với sysprof, bạn có thể cấu hình tất cả các ứng dụng đang chạy trên máy của mình, bao gồm cả ứng dụng đa luồng hoặc đa xử lý ...

Và sau khi chọn chi nhánh của các quy trình phụ mà bạn thấy thú vị.


Đối với Valgrind:
Với một số phòng tập thể dục nhiều hơn, dường như có thể hiển thị cho Valgrind một số chương trình mà chúng ta thường cài đặt từ nhị phân (ví dụ: OpenOffice ).

Có thể đọc từ FAQ của valgrind rằng Valgrindsẽ cấu các tiến trình con nếu explicitely yêu cầu.

... Ngay cả khi theo mặc định, cấu hình của nó chỉ theo dõi quá trình cấp cao nhất và vì vậy nếu chương trình của bạn được khởi động bởi tập lệnh shell , tập lệnh Perl hoặc một cái gì đó tương tự, Valgrind sẽ theo dõi trình bao, hoặc trình thông dịch Perl hoặc tương đương. ..

Nó sẽ làm điều đó với tùy chọn này được kích hoạt

 --trace-children=yes 

Tài liệu tham khảo bổ sung:


1
Không phải downvoter, nhưng hầu hết các mẹo này, trong khi tuyệt, không thực sự có liên quan ở đây. Đặt một câu hỏi thích hợp và tự trả lời, câu hỏi này được chào đón nhiều hơn ở đây. Google "câu trả lời tự xếp chồng" cho các nghi thức liên quan.
Blaisorblade

0

Bài đăng này của Alan Hargreaves mô tả phương pháp lược tả tập lệnh shell Bourne bằng cách sử dụng nhà cung cấp DTrace. Theo như tôi biết thì nó hoạt động với Solaris và OpenSolaris (xem: / bin / sh Nhà cung cấp DTrace ).

Vì vậy, đưa ra tập lệnh dtrace sau ( sh_flowtime.dtại GH dựa trên bản gốc ):

#!/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));
}

bạn có thể theo dõi dòng chức năng bao gồm cả lần delta.

Đầu ra mẫu:

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

Sau đó, sử dụng sort -nrk7lệnh, bạn có thể sắp xếp đầu ra để hiển thị các cuộc gọi tiêu thụ nhất.

Tôi không biết về bất kỳ đầu dò nhà cung cấp nào có sẵn cho các shell khác, vì vậy hãy thực hiện một số nghiên cứu (tìm kiếm GitHub?) Hoặc nếu bạn muốn đầu tư một thời gian, bạn có thể viết như vậy dựa trên ví dụ sh hiện có : (xem: Cách kích hoạt sh Nhà cung cấp DTrace? ).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.