Rainbow Engine

IT技術を分かりやすく簡潔にまとめることによる学習の効率化、また日常の気付きを記録に残すことを目指します。

C++ Linux

GDBの使い方をC++のプログラムのデバッグを例にご紹介

投稿日:2021年8月25日 更新日:

 

<目次>

(1) GDBの使い方をC++のプログラムのデバッグを例にご紹介
 (1-1) GDBのデバッグのシナリオ概要(例)
 (1-2) GDBのデバッグ手順(例)
 (1-3) その他の基本的なコマンド
 (1-4) エラー対応:Missing separate debuginfos, use: debuginfo-install

(1) GDBの使い方をC++のプログラムのデバッグを例にご紹介

本記事ではUnix系で使用するC++のデバッガであるGDB について、その基本的な使い方を実際の例を使ってご紹介いたします。

(1-1) GDBのデバッグのシナリオ概要(例)

こちらのサンプルプログラムはある整数nを指定すると「1/n^2」の1~nまでの和、つまり「1/(1^2) + 1/(2^2) + 1/(3^2) + 1/(4^2) + … + 1/(n^2)」を計算する簡単なプログラムですが、1カ所ロジックに誤りがあります。

(サンプル)

 
#include <iostream>
#include <cmath>

using namespace std;

int CalcPow(int n) {
    int powResult = 0;
    powResult = powResult * n * n;
    return powResult;
}

double CalcPowSum(int n) {
    double powSum = 0.0;
    for (int i = 1; i <= n; i++) {
        powSum += 1.0 / CalcPow(n);
    }
    return powSum;
}

int main() {

    cout << "[INPUT] Enter value of n : ";
    int n;
    cin >> n;
    cout << endl;

    double powsum = CalcPowSum(n);
    cout << "[RESULT] The value of PowSum is : " << powsum << endl;
}
 
階乗の計算をする関数「CalcPow」にバグがあり常に0を返してしまうために、常に無限が返却されてしまっています。
 

目次にもどる

(1-2) GDBのデバッグ手順(例)

●STEP0:前提条件

C++のプログラムを作成~コンパイルするためには「テキストエディタ」や「コンパイラ」が必要です。今回は「GCC」のコンパイラを使ってコンパイルを行う例をご紹介します。
 
・①既に導入されているか?の確認
 
GCCコンパイラが既に導入されているか?をチェックするには次のコマンドを実行します。
 
$ gcc –version
$ g++ --version
$ make –version

(図130)コマンドの出力例

・②導入されていない場合⇒新規導入
 
下記の記事に沿ってインストールを実施頂けたらと思います・
 
 

目次にもどる

●STEP1:プログラムの入手

下記のサンプルプログラムを拡張子「.cpp」(C++の拡張子)で保存して、ご自身のサーバや端末に配置します。
 
(サンプルプログラム)
#include <iostream>
#include <cmath>

using namespace std;

int CalcPow(int n) {
  int powResult = 0;
  powResult = n * n;
  return powResult;
}

double CalcPowSum(int n) {
  double powSum = 0.0;
  for (int i = 1; i <= n; i++) {
    powSum += 1.0 / CalcPow(n);
  }
  return powSum;
}

int main() {

  cout << "[INPUT] Enter value of n : ";
  int n;
  cin >> n;
  cout << endl;

  double powsum = CalcPowSum(n);
  cout << "[RESULT] The value of PowSum is : " << powsum << endl;
}
 
サーバ上の任意の場所に配置してviエディタ等で正常にアップロードされた事を確認しています。
(図131)①



(図131)②

●STEP2:コンパイル&実行結果の確認

次のコマンドでプログラムをコンパイル&実行します。
 
$ g++ -g rainbow.cpp -o rainbow
$ ./rainbow

(図132)

実行すると整数「n」の入力を求められますが、バグがあるためどんな値を入力してもinf(無限)になってしまいます。ここで異常を検知して、次のデバッグに進みます。
 

●STEP3:デバッガの起動

次のコマンドでデバッガを起動します。
(コマンド)
$ gdb rainbow

(コマンド実行結果)例

GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-114.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /tmp_rainbow/rainbow...done.
(gdb)

(図133)

●STEP4ブレークポイントの設置

「b [設置行]」でブレークポイントを設置します。今回は27行目に設置するため「b 27」と入力します。
(コマンド)
(gdb) b 27
 
(コマンド実行結果)例
Breakpoint 1 at 0x400969: file rainbow.cpp, line 27.
 
27行目は2乗のnまでの総和を計算する関数CalcPowSumが呼び出される下記の行になります。
int powsum = CalcPowSum(n);
(図134)

●STEP5:デバッグ実行の開始

・①デバッグ実行
次のコマンドで、プログラムのデバッグ実行を開始します。
 
(コマンド)
(gdb) run
 
(コマンド実行結果)例
Starting program: /tmp_rainbow/rainbow
[INPUT] Enter value of n :
 
(図135)①

(備考)
コマンドライン引数がある場合は、通常の引数ありの実行と同じようにrunコマンドの後ろに追加します。
 
・②引数を与える
プログラムの実行が開始されると、引数の入力を求められるので「4」を入力します。
 
[INPUT] Enter value of n : 4
↓
Breakpoint 1, main () at broken.cpp:27
27  int powsum = CalcPowSum(n);
 
(図135)②

この状態で、プログラムは最初に設置した27行目のブレークポイントで止まっています。

●STEP6:関数内にステップイン&調査

・①ステップイン
21行目の「CalcPowSum」内にステップインして内部の動きをチェックします。
 
(図136)①

(コマンド)
(gdb) step
(コマンド実行結果)例
CalcPowSum (n=4) at rainbow.cpp:13
13              double powSum = 0.0;
 
(図136)②
 

・②更にステップイン
「next」と「n」は同じ意味で、いずれもステップオーバーのコマンドです(関数にステップインせずに次に進む)。また「s」は「step」と同じ意味でステップインします。

なので、次の「next」⇒「n」で「CalcPowSum」関数内で更に次へと移動し、「s」で「CalcPow」内に更にステップインします。

(コマンド&実行結果)

(gdb) next
14              for (int i = 1; i <= n; i++) {
(gdb) n
15                      powSum += 1.0 / CalcPow(n);
(gdb) s
CalcPow (n=4) at rainbow.cpp:7
7               int powResult = 0;
(gdb)

 

(図137)

●STEP7:変数の値の確認

「CalcPow」内を更に辿っていくと、2乗の計算結果を格納する「powResult」が0である事(0*n*nなのでnが何であっても0になる)が確認できました(バグ)。なので修正のため一度quitで抜けます。
 
(コマンド&実行結果)例
 
(gdb) n
8               powResult = powResult * n * n;
(gdb) n
9               return powResult;
(gdb) print powResult
$1 = 0
(gdb) quit

(図138)

なので、ここを修正→再コンパイルして実行すると正しい結果を得る事ができます。
 
(コマンド&実行結果)例
 
(gdb) n
8               powResult = n * n;
(gdb) n
9               return powResult;
(gdb) print powResult
$1 = 16
(gdb)

(図139)

(1-3) その他の基本的なコマンド

上記でご紹介したコマンドに加え、基本的なコマンドを整理した表を掲載します。
 
(表)GDB基本的なコマンド
b [設置行番号] ●ブレークポイントの設置
(例)
「b 21」で21行目にブレークポイントを設置する。
run ●デバッグ実行
プログラムのデバッグ実行
step
(短縮形=s)
●ステップイン
・関数にステップインし、その関数内の最初のステートメントに移動します。
・関数が無い場面で実行すると、nextと同じ挙動になります。
(例)
main 21行目で「step」⇒CalcPowSumにステップイン
next
(短縮形=n)
●ステップオーバー
・関数にステップイン「せずに」、次のステートメントに移動します。
backtrace
(短縮形=bt)
●バックトレース
実行中のスレッドにてコールされた関数の一覧です。これを確認する事で、現在地点にどのように到達したか?の流れを把握する事ができます。
繰り返し ●コマンド繰り返し
・前と同じコマンドを繰り返す場合、Enterキーを押下するだけで繰り返しができます。
print ●変数の値を表示
・「print [変数名]」で変数の名前を表示します。
quit ●デバッガの終了
(図141)バックトレースの例

(1-4) エラー対応:Missing separate debuginfos, use: debuginfo-install

もしデバッグ中に「Missing separate debuginfos, use: debuginfo-install」のメッセージが出た場合の対応メモです。
 

●事象

デバッグ時に「run」コマンドでデバッグ実行を開始した時に「Missing separate debuginfos, use: debuginfo-install」のメッセージが表示された。
 
(図151)

画面を見ると「use: debug-info ~」と指示があるので、それに沿って対処します。

●対処

 
画面の指示に従い、以下の不足資源をインストールするコマンドを実行します。
sudo debuginfo-install glibc-2.17-260.el7_6.4.x86_64 libgcc-4.8.5-44.el7.x86_64 libstdc++-4.8.5-44.el7.x86_64
 
(図152)
(図153)
(図154)
(図155)
 

目次にもどる

Adsense審査用広告コード


Adsense審査用広告コード


-C++, Linux

執筆者:


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

関連記事

Valgrindの使い方や見方について(基礎編)

  <目次> (1) Valgrindの使い方や見方について(基礎編)  (1-1) メモリリークのチェックのやり方  (1-2) 「valgrind」コマンドの基本的な使い方  (1-3) …

C++の「::」の記載の意味について

  <目次> (1) C++の「::」の記載の意味について  (1-1) 「::」の意味は?  (1-2) サンプル①:スコープ演算子「::」を使ってグローバル変数にアクセス  (1-3) …

C++のfork関数の構文や使い方について

  <目次> (1) C++のfork関数の構文や使い方について  (1-1) fork()関数とは?概要や目的  (1-2) fork()関数の構文  (1-3) fork()関数のサンプ …

CentOS7で日本語を入力するための設定手順

<目次> (1) CentOS7で日本語を入力するための設定手順  (1-1) STEP1:epelリポジトリの導入  (1-2) STEP2:パッケージのインストール  (1-3) STEP3:入力 …

CentOS6にVNCServerでリモート接続して日本語入力を可能にする方法

(0)目次&概説 (1) 記事の目的 (2) 日本語化の対応手順  (2-1) ibus系のインストール  (2-2) dbusインストール+エラー回避  (2-3) mocz系パッケージのインストー …

  • English (United States)
  • 日本語
Top