linuxユーティリティ----systemtap

12143 ワード

概要
SystemTapはLinuxシステムのパフォーマンスまたは機能の問題を診断するオープンソースソフトウェアです.これにより、実行時のLinuxシステムの診断調整が容易になり、簡単になります.これにより、開発者やデバッグ担当者は、新しいカーネルの再コンパイル、インストール、再起動などの煩わしいステップを必要としなくなります.
 一般的にプログラムを書くと、対応するレベルのログが追加され、gdbを使用することなく、問題を特定したり、コードの経路を観察したりするのに役立ちます.しかし、システムプログラミングでは、ログを狂わせることはできません(ioパスにログを追加する実験を経て、rsyslogはよく掛けられます.また、/var/log/meassagesの中の情報が多すぎると、他の心痛エラーを探すのが大変です).また、多くの呼び出しスタックがkernel spaceにあるので、普通のデバッグ手段は窮屈になります.
 この時systemtapは役に立つ.彼はカーネル関数にprobeプローブを加え、kernel space関数呼び出しを統計的にまとめ、介入することもできる.しかしuser spaceのデバッグサポートはあまりよくありません.
環境設定
  •  私の実験環境はCentos 7で、カーネルバージョンkernel-3.10.0-5114.26.2.el7.x86_64、バージョンによって公式サイトに以下の対応するパッケージをダウンロードします:
  • vault.centos.org
  • debuginf.centos.org

  • [root@xt2 ~]# rpm -qa |grep kernel kernel-headers-3.10.0-514.16.1.el7.x86_64 kernel-debuginfo-common-x86_64-3.10.0-514.26.2.el7.x86_64 kernel-3.10.0-514.26.2.el7.x86_64 kernel-debuginfo-3.10.0-514.26.2.el7.x86_64 kernel-devel-3.10.0-514.26.2.el7.x86_64
    これらのkernelパッケージを詰めるときは、既存のパッケージ、kmod-20-9を置き換える可能性があります.el7.x86_64.rpm kmod-libs-20-9.el7.x86_64.rpm linux-firmware-20160830-49.git7534e19.el7.noarch.rpm xfsdump-3.1.4-1.el7.x86_64.rpm xfsprogs-4.5.0-8.el7.x86_64.rpm
    地元の古いrpm-e--nodeps xxxxを削除して新しいものを入れます
  •  取付工具yum install systemtap
  • stapスクリプトの編集を開始
    簡単な例は次のとおりです.
    #!/usr/bin/env stap
    
    global fuck = 0
    global g_ino = 0
    
    probe begin {
        printf("probe begin
    ") } probe module("fuse").function("fuse_finish_open") { inode = pointer_arg(1) ino = @cast(inode, "struct inode")->i_ino g_ino = ino printf("coming fuse_finish_open ino: %lu
    ", ino) } probe module("fuse").function("fuse_file_mmap") { printf("coming fuse_file_mmap,,,,
    ") } probe module("fuse").function("fuse_link_write_file") { printf("coming fuse_link_write_file
    ") # print_backtrace() } probe kernel.function("generic_file_aio_write"){ fuck = 1 nr_segs = ulong_arg(3) pos = ulong_arg(4) printf("coming generic_file_aio_write nr_segs: %lu %lu
    ", nr_segs, pos) } probe kernel.function("page_cache_tree_insert"){ ino = $mapping->host->i_ino if (ino == g_ino) { index = $page->index nrpages = $mapping->nrpages printf("coming page_cache_tree_insert ino: %lu index:%lu pages: %lu
    ", ino, index, nrpages) } } probe kernel.function("page_cache_tree_delete"){ ino = $mapping->host->i_ino if (ino == g_ino) { index = $page->index nrpages = $mapping->nrpages printf("coming page_cache_tree_delete ino: %lu index:%lu pages: %lu
    ", ino, index, nrpages) #print_backtrace() } } probe kernel.function("__set_page_dirty_nobuffers"){ page = pointer_arg(1) index = @cast(page, "struct page")->index printf("coming set_page_dirty_nobuffers: %lu
    ", index) #print_backtrace() printf("---------------------------------------- page dirty
    ") } probe module("fuse").function("fuse_release"){ fuck = 1 printf("coming fuse_release******************
    ") print_backtrace() } #probe kernel.function("tag_pages_for_writeback") #{ # if (fuck == 1) { # index = ulong_arg(2) # end = ulong_arg(3) # printf("coming tag_pages_for_writeback: index %lu end: %lu
    ", index, end) # } #} #probe kernel.function("pagevec_lookup_tag"){ # if (fuck == 1) { # tag = int_arg(4) # printf("pagevec_lookup_tag %d
    ", tag) # } #} #probe kernel.function("pagevec_lookup_tag").return ? #{ # printf("pagevec_lookup_tag return %u
    ", $return) #print_backtrace() #} #probe kernel.function("pagevec_lookup_tag").return{ # printf("find_get_page return %u
    ", $$return) # print_backtrace() #} probe module("fuse").function("fuse_writepage"){ page = pointer_arg(1) index = @cast(page, "struct page")->index printf("coming fuse_writepage: %lu
    ", index) } probe module("fuse").function("fuse_writepages"){ printf("coming fuse_writepages:
    ") } probe module("fuse").function("fuse_send_write_pages"){ printf("coming fuse_send_write_pages:
    ") print_backtrace() } #probe kernel.function("write_cache_pages"){ # printf("coming write_cache_pages:
    ") #} probe module("fuse").function("fuse_writepage_locked"){ page = pointer_arg(1) index = @cast(page, "struct page")->index printf("coming fuse_writepage_locked: %lu
    ", index) print_backtrace() printf("--------------------------------------------------------


    ") } probe module("fuse").function("fuse_writepages_fill"){ page = pointer_arg(1) index = @cast(page, "struct page")->index printf("coming fuse_writepages_fill: %lu
    ", index) } probe module("fuse").function("fuse_write_update_size"){ printf("coming fuse_write_update_size: ino %lu size:%lu pos:%lu
    ", $inode->i_ino, $inode->i_size, $pos) } #probe module("fuse").function("queue_request") #{ # printf("queue_request ...
    ") #print_backtrace() #} #probe kernel.statement("*@mm/page-writeback.c:1941") #{ # if (fuck == 1) { #ino = $page->mapping->host->i_ino # if (ino == g_ino) # printf("coming lock_page: index %lu
    ", $done_index) # } #} #probe module("fuse").function("fuse_flush_dirty"){ # inode = pointer_arg(2) #ino = @cast(inode, "struct inode", "")->i_ino # printf("coming fuse_flush_dirty ino: %lu
    ", ino) # printf("coming fuse_flush_dirty
    ") #} #probe kernel.function("__filemap_fdatawrite_range"){ # mapping = pointer_arg(1) # ino = @cast(mapping, "struct address_space")->host->i_ino # printf("coming __filemap_fdatawrite_range ino: %lu
    ", ino ) #} #probe kernel.function("generic_writepages"){ # mapping = pointer_arg(1) # ino = @cast(mapping, "struct address_space")->host->i_ino # printf("coming generic_writepages ino: %lu
    ", ino) #} #probe kernel.function("write_cache_pages"){ # mapping = pointer_arg(1) # ino = @cast(mapping, "struct address_space")->host->i_ino # printf("coming write_cache_pages ino: %lu
    ", ino) # printf("--------------------------------------------------------
    ") # print_backtrace() # printf("--------------------------------------------------------


    ") #} #probe module("fuse").function("fuse_writepage_locked"){ # printf("--------------------------------------------------------
    ") # print_backtrace() # printf("--------------------------------------------------------


    ") #} probe end { printf("probe end") } ( systemtap ):/usr/share/systemtap/examples :https://sourceware.org/systemtap/wiki/WarStories

    一般的な方法
    1.トレース可能な関数または変数のリスト
    名前にnitを含むカーネル関数を検索します:stap-l'kernel.function("nit")'名前にnitを含むカーネル関数と変数を検索:stap-L'kernel.function("nit")'

    kernelにwriteのあるすべての関数stap-l'kernelをリストします.function("write")' >/tmp/kk
      fuse    write     
    

    stap -l 'module("fuse").function("write”)'
    2.スクリプトに関数を追加します.ここでは、パラメータの取得方法について説明します.
    a.最も簡単な方法:まず、この関数のキャプチャ可能なパラメータテーブルを取得し、$変数名で変数の内容を直接取得することができます.
    [root@xt1 systemtap]# stap -L 'kernel.function("do_fork")'
    kernel.function("do_fork@kernel/fork.c:1685") $clone_flags:long unsigned int $stack_start:long unsigned int $stack_size:long unsigned int $parent_tidptr:int* $child_tidptr:int*
    
    test.stp
    --------------------------------------------------------------------------------------------------------
    global proc_counter
    
    probe begin {
        print("Started monitoring creation of new processes...Press ^C to terminate
    ") printf("%-25s %-10s %-11s %-5s %-s
    ", "Process Name", "Process ID", "Clone Flags", "start", "size") } probe kernel.function("do_fork") { proc_counter++ printf("%-25s %-10d 0x%-11x %-5lu %lu
    ", execname(), pid(), $clone_flags, $stack_start, $stack_size) } probe end { printf("
    %d processes forked during the observed period
    ", proc_counter) } : long do_fork(unsigned long clone_flags, unsigned long stack_start, unsigned long stack_size, int __user *parent_tidptr, int __user *child_tidptr)

    b.第2の方法でパラメータを取得することもでき、debuginfo、すなわちDWARF-less probingが欠けている場合はuint_arg(),pointer_Arg()とulong_arg()などで取得します.これらの関数は、現在取得するパラメータが何番目かを指定する必要があります.番号は1からです.たとえばasmlinkage ssize_t sys_read(unsigned int fd,char__user*buf,size_t count)ではuint_arg(1)はfdの値を取得し、pointer_Arg(2)はbufのアドレス値、ulong_を取得するArg(3)はcountパラメータを取得する
    c.プロセスを通過する場合.syscall、process("PATH").Syscallまたはprocess(PID).Syscallがprobeシステム呼び出しに来た場合、arg 1,$arg 2等により対応するパラメータ値を取得することができる.
    3.インライン関数または関数が取得できない場合の変数の値の取得方法
  • インライン関数:この関数については、関数自体が入っているかどうかをキャプチャすることができますが、パラメータと戻り値は印刷できません.
  • は関数をキャプチャできません.関数はマクロ定義を使用する場合があり、この関数をキャプチャできませんが、その近くに関連する変数の値を取得する必要があります.上記の2つの状況に対して、私たちは切り札「statement」を使ってソースコードのある行に直接位置決めしてこの変数を見ることができます!
  • probe kernel.statement("*@mm/page-writeback.c:1941")
    {
        if (fuck == 1) {
          ino = $page->mapping->host->i_ino
          if (ino == g_ino)
            printf("coming lock_page: index %lu 
    ", $done_index) } }

    4.一般的な関数
    1.execname()
             ,         
    2. pid()
           PID
    3.pp()
         probe 。   probe process.syscall,process.end { /* scripts */},     pp()     "process.syscall" "process.end"。
    4.probefunc()
        probe     。  probe sys_read  , probe           sys_read。            pp()            。
    5.tid()
           ID
    6.cpu()
        CPU ID
    7.gettimeofday_s()
        Unix  
    8.get_cycles()
            
    9.ppfunc()
        probe     。 probe              ,       probe      。
    10.print_backtrace()
             
    11.print_ubacktrace()
              
    12.thread_indent()
             ,   “         (  id):(  )”,    probe          ,         。          ,         。       ,        。          (      )           thread_indent    。
    13.target()
                 ID。    stap -c -x    。
    

    4.テクニック
    a. "."文字のスキップコネクタ関数で返される文字列と定数文字列をつなぎ合わせる場合は、両者の間に「.」を追加します.例えばprobefunc()である."123". 「.」演算子は「.=」もサポートします.つまり、パッチ後に値を割り当てます.b.stapコマンドラインパラメータを取得コマンドラインパラメータの正確な値を取得する場合は、2....$に表示されます.コマンドラインパラメータを文字列に変換する場合は、@1、@2...@に表示されます.c.next操作probe関数で条件が満たされていないことが判明した場合、今回のprobeの実行を終了し、次のイベントの到来を待つ.例は次のとおりです.
    global i
    probe begin {
        printf("SystemTap Scripts start.....
    "); } probe kernel.function("sys_read") { ++i; if (i % 2) { next; } printf("i = %d
    ", i); }

    d.$$vars適切であれば、現在表示されているすべての変数のリストを$$varsで取得できます.リストの一部のメンバーの値に「?」が表示されている場合、これは、現在これらの変数が初期化されておらず、アクセスできないことを示します.e、call、inline接尾辞の違いcall接尾辞を付けると、現在のprobeの関数が非インライン関数である場合にのみイベントがトリガーされます.例えば、インライン関数pskb_may_pull()のprobeポイントにcall接尾辞を付けると、イベントはトリガーされません.非インライン関数のprobe点にはinline接尾辞を付けることはできません.そうしないと、コンパイル時にエラーが表示されます.インライン関数のprobeイベントをトリガーしたい場合は、call接尾辞を付けることはできません.callとinlineの接尾辞が追加されていない場合、カーネル関数と非インライン関数のprobeイベントがトリガーされます.f、出力「%」文字systemtapでエスケープ文字を使用して「%」を出力しても効果はなく、コンパイル時にエラーが発生し、「%%%」を使用して「%」を出力することができます.
    参考:1.https://blog.csdn.net/zhangskd/article/details/25708441 2.https://www.ibm.com/developerworks/cn/linux/l-cn-systemtap3/3.https://www.jianshu.com/p/84b3885aa8cb