gdbによるredisソースコードのデバッグと観察


概要
この文書では、gdbを介してredisのソースコードをデバッグする方法について説明します.ソースコードを表示するだけでなく、gdbを介して実際のシーンでコードがどのように動作するか、メモリがどのように変化するかを観察することは、redis-serverの動作メカニズムを理解するために非常に必要である.redis-serverの一般的なコマンドの実行メカニズムのデバッグを行う場合は、c/c++プログラミングに精通する必要はありません.gdbの基本的なコマンドを知るだけでいいです.
gdb紹介
gdbでredis-serverをデバッグする場合は、基本的なgdbコマンドを知る必要があります.たとえば、ブレークポイントの作成方法、構造体の内容の印刷方法、サブプロセスの実行を追跡する方法などです.このセクションでは、redisの世界に入るための基本的なgdbコマンドについて説明します.
  • gdbを先に起動し、実行コードを指定します(redis-serverは事前にコンパイルされています).
  • $ gdb ./src/redis-server (gdb)
  • gdb
  • は、以下のコマンドで実行する.
    コマンド名
    機能
    r
    先ほどロードしたバイナリプログラムを実行し、パラメータを指定できます
    b
    ブレークポイント、後のパラメータは関数、または行数です.
    n
    単一ステップで実行しますが、関数には入りません.
    s
    単一ステップ実行、呼び出し関数へ
    bt
    現在の呼び出しスタックの表示(関数の呼び出しスタックと現在実行されている場所を表示するのに役立ちます)
    l
    コードを表示します.パラメータは関数名または行数です.
    p
    変数または構造体の値を印刷
    他のコマンドはgdbマニュアルを表示できます.
    gdbでredis-serverをデバッグする
    redis-serverソースコードのコンパイル
    redis-4.0.9をダウンロードし、解凍してredisディレクトリに入り、次のように直接makeします.
    cd redis-4.0.9
    make

    コンパイルされたバイナリはsrcで、redis-4.0.9ディレクトリの下にredisがあることに注意してください.confファイル.redis-serverを実行するときのプロファイルです.
    注意:gdbでコードをデバッグする場合は、コンパイル時にgccの後に-g-ggdbというオプションを付ける必要があります.redis-serverのコンパイルオプションではデフォルトで追加されるので、直接makeでいいです.
    コンパイルが完了したら、srcの下に、他にもいくつかの実行可能なファイルがコンパイルされているはずです.
    redis-server: redis         
    redis-cli   :  redis      
    redis-benchmark
    redis-sentinel
    ...

    本稿では、2つのredis-serverとredis-cliを使用します.
    デバッグの開始
    コードをコンパイルしてredis-serverのデバッグを始めました
    $ gdb src/redis-server 
    (gdb)
    

    gdbでsetコマンドの実行手順を表示する
  • gdbによりredis-server
  • を起動する
    setコマンドの実行手順を表示するには、まずsetコマンドを実行する関数のエントリにブレークポイントを付けます.次に、この処理関数に入り、単一ステップで実行します.server.cコードには、コマンド処理のリスト関数があります.以下のようにします.
    struct redisCommand redisCommandTable[] = {
        {"module",moduleCommand,-2,"as",0,NULL,0,0,0,0,0},
        {"get",getCommand,2,"rF",0,NULL,1,1,1,0,0},
        {"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0},
        {"setnx",setnxCommand,3,"wmF",0,NULL,1,1,1,0,0},
        {"setex",setexCommand,4,"wm",0,NULL,1,1,1,0,0},
    ... ...

    以上のコードからsetコマンドの実装関数はsetCommandであり、この関数の入り口にブレークポイントを打つことができます.
    //     
    (gdb) b setCommand 
    
    //   redis-server
    (gdb) r ./redis.conf

    この場合、gdbはredis-serverの接続待ち場所をブロックします.redisクライアント接続サービス側がないため、redis-cliを起動し、setコマンドを送信する必要があります.
  • redis-cli
  • を起動
    新しい端末を開き、redisをコンパイルしたばかりのディレクトリに入り、redis-cliを起動します.
    cd redis-4.0.9/src
    $ ./redis-cli 
    127.0.0.1:6379> set k1 "v123"
  • setコマンドの実行メカニズムをgdbのインタフェースで観察すると、コードはsetCommand関数エントリに実行されていることがわかります.gdb端末で実行スタックを表示する:
  • (gdb) bt
    #0  setCommand (c=0x102016000) at t_string.c:102
    #1  0x000000010000cd53 in call (c=0x102016000, flags=15) at server.c:2229
    #2  0x000000010000d61e in processCommand (c=0x102016000) at server.c:2510
    #3  0x000000010001dd76 in processInputBuffer (c=0x5b91a431) at networking.c:1354
    #4  0x00000001000051ee in aeProcessEvents (eventLoop=0x1005289b0, flags=11) at ae.c:440
    #5  0x000000010000552b in aeMain (eventLoop=0x5b91a431) at ae.c:498
    #6  0x0000000100010680 in main (argc=1, argv=0x0) at server.c:3894

    この関数に入り、次のステップで実行します.
    (gdb) n
    138     c->argv[2] = tryObjectEncoding(c->argv[2]); //      : string value    

    関数の内部に入るには、sコマンドを直接使用します.
    (gdb) s
    tryObjectEncoding (o=0x100306600) at object.c:384
    384     serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);

    変数の値を印刷します.
    (gdb) p o
    $1 = (robj *) 0x100306600 // o   
    
    //              
    (gdb) p *o
    $2 = {type = 0, encoding = 8, lru = 9544752, refcount = 1, ptr = 0x100306613}
    (gdb) p o->type

    コマンド処理の全プロセスに至るまで、processCommand関数にブレークポイントを打つことができます.そして、上記と同様にワンステップで実行します.
    まとめ
    本論文では,gdbを介してredis−serverをデバッグし,redis−serverの内部実行を観察する方法について述べた.しかし、マルチプロセスのデバッグなど、より複雑なgdbのデバッグテクニックについては説明していません.