LoginSignup
2
6

More than 3 years have passed since last update.

LLDBでのデバッグ備忘録

Last updated at Posted at 2020-07-31

ある程度長いプログラムを書くようになってくると、エラーを探すのが難しくなってきます。

そんな時に必要となってくるのが「デバッガ」です。

多分これがないとやってられない、、???と思います。

ほぼ自分用ですが、LLDBの使い方まとめをやっていきます。

Macにはデフォルトで入っていそうな雰囲気なので、特に環境構築は必要ないかと思います。

ちなみに、ファイルを実行するたびに動作が異なる、、、、、、とかバグの原因が特定できない、、、、、、という時は、Valgrindを使うと、一発でメモリリークが分かって最高なのでぜひ試してみてください。
(LLDBでもメモリ系のコマンドがあるのでもしかしたらLLDBでも分かるかも??)


今回、例として使うファイルはこんな感じです

test.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

void dainyu(int aa, int bb);

int main(void)
{
   double a = 3.0 ;
   double b = 5.0 ;
   double c = 7.0 ;

   dainyu(1,2);
   dainyu(2,3);
}
//--------------------------------------------------------
void dainyu(int aa, int bb){
    const int num1 = aa ;
    static int num2 = 0;
    num2 = 0;
    num2 += 2;

    printf("aa(const) :%d, bb(static) : %d \n", num1, num2);
}

conststaticの違いを実験するプログラムです。

ちなみに、出力は

aa(const) :1, bb(static) : 2 
aa(const) :2, bb(static) : 2 

となります。
つまり、staticにしておくと、関数が終了した後もnum2は値を保持し続けます。
つまり、static int num2 = 0;の初期化が行われるのは、初めてdainyu()が呼び出された際の一回のみです。

前準備

デバッグをするためには、コンパイルの際に-gを指定する必要があります。

gcc -g test.c

(蛇足ですが、最適化のための-O3と、警告をいっぱい出してくれるための-Wallは毎回入れた方がいいでしょう)

起動

先ほどのコンパイルでtestという名前の実行ファイルが出たとします。

起動するにはlldb [実行ファイル]のコマンドを打ちます。

lldb ./test
(lldb) target create "./test"
Current executable set to '/Users/K-eno/Scripts/test' (x86_64).

よく使うコマンド一覧

[実行]

(lldb) r

これをやると、シンプルに実行できます。

(lldb) r
Process 18038 launched: '/Users/KeigoEnomoto/Scripts/hydrodynamics/MPS/test' (x86_64)
aa(const) :1, bb(static) : 2 
aa(const) :2, bb(static) : 2 
Process 18038 exited with status = 0 (0x00000000) 

[コードの表示]

(lldb) l

数行ごとにtest.cのコードの中身を見ることができます。

[ブレイクポイントの設定]

行で指定

(lldb) b 15
Breakpoint 1: where = test`main + 32 at test.c:15:8, address = 0x0000000100000ec0

これで、15行目にブレイクポイントを打つことができました。
b の部分はbrとかbreakpointでもいいです
その後実行するとこんな感じです。

(lldb) r
Process 18105 launched: '/Users/KeigoEnomoto/Scripts/hydrodynamics/MPS/test' (x86_64)
Process 18105 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100000ec0 test`main at test.c:15:8
   12   int main(void)
   13   {
   14   
-> 15   double a = 3.0 ;
   16   double b = 5.0 ;
   17   double c = 7.0 ;
   18   
Target 0: (test) stopped.

ちゃんと、15行目でストップできていますね。

また、複数ファイルをコンパイルしている場合は、b main.c : 20のようにすると、main.cファイルの20行目にブレイクポイントを打つというように指定することができます。

関数名で指定

br set -n [funciton]

[function]の所に関数名を入れます。

実際に使ってみるとこんな感じです。

(lldb) br set -n dainyu
Breakpoint 1: where = test`dainyu + 14 at test.c:27:22, address = 0x0000000100000f0e
(lldb) r
Process 18177 launched: '/Users/KeigoEnomoto/Scripts/hydrodynamics/MPS/test' (x86_64)
Process 18177 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100000f0e test`dainyu(aa=1, bb=2) at test.c:27:22
   24   }
   25   
   26   void dainyu(int aa, int bb){
-> 27       const int num1 = aa ;
   28       static int num2 = 0;
   29       num2 = 0;
   30       num2 += 2;
Target 0: (test) stopped.

関数の1行目まで入っていますね
その後ステップ実行すれば分かることですが、この場合、一つ目のdainyu(1,2)にブレイクポイントが打たれています。

ちなみに、breakpoint listで設定したブレイクポイントを全て出力することができます。

[ステップ実行]

ブレイクポイントを何処かに打った後にsもしくはn(next)を打つとステップごとに実行することができます。
s -c <count> のようにすると、ステップ回数を指定することができます。
その他のオプションはh sと打てば出てきます。

また、c(continue)を用いると、次のブレイクポイントまで(設定していない場合は最後まで)実行することができます。

[変数の表示]

frame varuableを使うと、変数の一覧とその中身を表示することができます。

(lldb) frame variable

実際にはこんな感じです。

(lldb) n
Process 18352 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x0000000100000eca test`main at test.c:17:8
   14   
   15   double a = 3.0 ;
   16   double b = 5.0 ;
-> 17   double c = 7.0 ;
   18   
   19   //printf("re : 5.0 , w(3) : %lf , w(7) : %lf ", weight(a,b), weight(c,b));
   20   //printf("a : %lf, b : %lf, c : %lf \n",a,b,c);
Target 0: (test) stopped.
(lldb) frame variable
(double) a = 3
(double) b = 5
(double) c = 0

まだ、16行目までしか実行はされていないので、cには7.0が代入されていません。

また、その後ろに変数を指定して見ることもできます。

(lldb) frame variable a
(double) a = 3


  • 変数の中身だけを見たい場合はpoコマンドを使っても実行できます。
(lldb) po b
5


  • 構造体を出力する際には、pコマンドを使用します。
(lldb) p *parameters
(simulation_parameters) $6 = {
  DIM = 2
  ParticleDistance = 0.025000000000000001
  FluidDensity = 1000
  KinematicViscosity = 9.9999999999999995E-7
  GRAVITY_SWITCH = 1
  DT = 0.001
  FINISH_TIME = 2
  OUTPUT_INTERVAL = 20
}

(これは、全く別のコードで使っている構造体ですが、、、、)


  • 配列の中身を見るときには少し特殊なコマンドを打つ必要があるようです。
      (他にいいのがあったら教えて欲しい、、、)
(lldb) p *(double (*)[10])Pressure
(double [10]) $1 = ([0] = 0, [1] = 0, [2] = 0, [3] = 0, [4] = 0, [5] = 0, [6] = 0, [7] = 0, [8] = 0, [9] = 0)

これだと、Pressureという配列のうち、先頭から10個の値を見ることができます。

[終了]

qもしくはquitで終了することができます。

後書き

参考記事に非常に似通ってしまったので、パブリックにするかは悩みどころではありますが、いいメモにはなりました。
適宜、便利なコマンドは追加していこうと思います。

参考記事

LLDBを使ってCのデバッグをする
LLDBとかいう次世代高性能デバッガ

2
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
6