結論

デバッグとは、プログラムの誤り(バグ)の原因を突き止めて直す作業です。「再現→切り分け→原因→修正→確認」の順に進めます。

この記事でわかること

  • デバッグの5つの手順の図と、方法8種の早見表
  • pdb・node inspect・jdb で同じバグを止めた出力
  • エラー文の読み方と、16コミットを4回で絞るbisect

2026年10月23日更新|Python 3.11・Node.js 22・JDK 21で実行確認

バグ(bug)は英語で「虫」のことで、デバッグ(debug)は「虫を取り除く」という意味の言葉です。勘で直すのではなく、次の順に事実を集めて原因を確かめます。

flowchart TD
    A[1 再現する<br>同じ手順で必ず起こす] --> B[2 切り分ける<br>どこから値がずれるか]
    B --> C[3 原因を突き止める<br>デバッガー・ログで見る]
    C --> D[4 直す]
    D --> E{5 確かめる<br>直った・ほかは壊れていない}
    E -->|いいえ| B

急いでいる人向けに、よく調べられることの答えを先にまとめます。

知りたいこと 答え 詳しい節
デバッグとは バグの原因を見つけて直す作業 デバッグとは
読み・書き方 デバッグ(debug)。「デバック」は誤り 語源
テストとの違い テストは見つける、デバッグは原因を直す テストとの違い
やり方 再現→切り分け→原因→修正→確認 手順
よく使う方法 print・ログ・デバッガー・git bisect 方法の早見表
ブレークポイント 実行を止める場所の目印 デバッガーの用語
Python では breakpoint() か python -m pdb pdb
VS Code では F9 で止める場所、F5 で開始 VS Code

確認環境:Ubuntu 24.04.4 LTS。同じバグ(合計金額が合わない)を入れた小さなプログラムを、Python 3.11.15 の pdb、Node.js v22.22.2 の node inspect、OpenJDK 21.0.10 の javac と jdb、GCC 13.3.0 と GDB 15.1 で実際に止めて変数を見ました。ログは Python の logging、二分探索は Git 2.43.0 の git bisect run で実行しました。VS Code のデバッガーの操作は Visual Studio Code の公式ドキュメント(2026年10月21日更新)、pdb・Node.js・jdb・git bisect のコマンドは各公式ドキュメントで、語源は米国スミソニアン博物館の収蔵品の説明で確かめました(確認日 2026年10月23日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「デバッグとは|意味・手順・方法をPython・JS・Javaの実例で解説」

デバッグとは(意味・語源・読み方)

この節では、デバッグという言葉の意味と、どこから来た言葉なのかが分かります。

デバッグ(debug)とは、プログラムが思ったとおりに動かないとき、その原因(バグ)を見つけて直す作業のことです。 NTT東日本の用語解説も、プログラムの欠陥や誤りを見つけて直すことをデバッグと説明しています。

「直す」だけがデバッグではありません。「期待した結果」と「実際の結果」のずれが、プログラムのどこで生まれたのかを突き止めるまでが、時間のかかる部分です。エラーが出た行と、原因のある行が違うこともよくあります。

語源:bug(虫)と de(取り除く)

11870年代

発明家のエジソンが、電気回路の不具合を「bug」と呼んでいた(スミソニアン博物館の説明)。

21947年

ハーバード大学の計算機 Mark II の部品に挟まった蛾を、技術者が記録ノートに貼り「first actual case of bug being found」と書いた。

3今の意味

bug はプログラムの誤り、debug は de(取り除く)+bug で「誤りを取り除く」。

スミソニアン博物館の説明によると、蛾を貼ったノートの技術者のチームには、プログラマーのグレース・ホッパーがいました。「本物の(actual)虫が見つかった最初の例」という書き方からも分かるように、bug という言葉はその前から使われていました。

正しい書き方は「デバッグ」で、「デバック」は誤りです。 debug の g の音なので、濁ります。バグを取る道具(ソフトウェア)はデバッガー(debugger)と呼びます。

「デバッグ」の意味の見分け表

「デバッグ」は、場面によって少し違う意味で使われます。

場面 意味 この記事
プログラミング バグの原因を見つけて直す作業 扱う
ゲーム開発 遊んで不具合を探す確認作業(デバッガーと呼ぶ仕事) テストとの違いで触れる
スマホの「USB デバッグ」 Android を開発用 PC とつなぐ設定 FAQ
アプリの「デバッグモード」 詳しいエラーやログを出す動かし方 FAQ
デバッガー 実行を止めて中身を見る道具 扱う

バグの種類(構文エラー・実行時エラー・論理エラー)

この節では、バグを3つに分けたときの違いと、それぞれで実際に出たメッセージが分かります。

バグは「動く前に止まる」「動いている途中で止まる」「止まらないが結果が違う」の3つに分けると、探し方を選びやすくなります。

1構文エラー

書き方の誤り。実行の前(コンパイルや読み込みのとき)に止まり、行の場所が出る。いちばん見つけやすい。

2実行時エラー

動いている途中で例外が出て止まる。0で割る、null を読む、配列の外を読むなど。止まった場所はメッセージに出る。

3論理エラー

止まらずに最後まで動くが、結果が違う。メッセージが出ないので、デバッグの手間がいちばんかかる。

Python 3.11.15 で、それぞれを起こしたときの出力です。

# 構文エラー(syn.py)
def f(:
    return 1
  File "~/demo/syn.py", line 1
    def f(:
          ^
SyntaxError: invalid syntax
# 実行時エラー(nm.py):変数名の打ち間違い
total = 0
print(totla)
Traceback (most recent call last):
  File "~/demo/nm.py", line 2, in <module>
    print(totla)
          ^^^^^
NameError: name 'totla' is not defined. Did you mean: 'total'?

論理エラーは、この記事の例題(合計金額が合わない)がそれにあたります。エラーは1行も出ません。

ほかにも、次のような分け方で呼ばれるバグがあります。

呼び方 どんなバグか 探し方の目安
境界値のバグ 0件・1件・最後の1件など、端でだけ起きる 0・1・最大で試す
デグレード(デグレ) 直したら、前は動いていた別の所が壊れた テストを流し直す・git bisect
環境依存のバグ 自分の PC では起きず、本番だけで起きる 版・設定・データを比べる
タイミングのバグ 並行処理などで、たまにだけ起きる ログで記録する
性能のバグ 結果は正しいが遅い・メモリを食う 計測ツールで測る

デバッグとテストの違い

この節では、よく混ざる「テスト」と「デバッグ」の役割の違いが分かります。

テストは「バグがあるか」を見つける作業、デバッグは「なぜ起きるか」を突き止めて直す作業です。 失敗したテストは、そのままデバッグの「再現手順」になります。

比べる点 テスト デバッグ
目的 不具合があるかを見つける 不具合の原因を見つけて直す
始まり 仕様・期待する結果 再現した不具合
結果 合格か不合格か 直したコードと原因の説明
主に使うもの テストケース・テストコード デバッガー・ログ・コードを読む
例 合計が 1160 円になるか確かめる 400 円になる理由を突き止める

ゲーム開発では、遊んで不具合を探す確認の仕事を「デバッグ」、その担当を「デバッガー」と呼ぶことがあります。これは、上の表では「テスト」の役割にあたります。

テストの種類は、関連記事「単体テストと結合テストの違い|pytestの実例と実行結果で比較」で扱っています。

デバッグの手順(再現→切り分け→原因→修正→確認)

この節では、どの言語でも同じに使えるデバッグの進め方が分かります。

1

再現する

同じ入力・同じ手順で、毎回バグが起きる状態を作る。手順・入力・期待した結果・実際の結果をメモする。再現できないうちに直すと、直ったかどうかを確かめられない。

再現

2

切り分ける

どこまでは正しく、どこからおかしいのかを、範囲を半分ずつに絞って探す。コード・入力データ・設定・ライブラリの版のどれが原因かも分ける。

範囲を絞る

3

原因を突き止める

「この変数がここで違う値になるはず」と仮説を立て、print・ログ・デバッガーで実際の値を見て確かめる。仮説は1つずつ確かめる。

仮説と確認

4

直す

症状が出た場所ではなく、原因の場所を直す。なぜこの修正で直るのかを説明できる状態にする。

修正

5

確かめる

1の再現手順でバグが消えたことと、ほかの機能が壊れていないこと(デグレードが無いこと)を確かめる。再現手順はテストコードにして残す。

確認

コードを読むだけで誤りを探す方法を「机上デバッグ」、デバッガーなどの道具を使って動かしながら探す方法を「マシンデバッグ」と呼ぶこともあります。NTT東日本の用語解説は、2つを組み合わせると問題の場所を早く見つけられると紹介しています。

デバッグの方法の早見表(print・ログ・デバッガー・git bisect)

この節では、デバッグの方法を一覧で比べ、どれを使えばよいかを選べます。

方法 何をするか 向いている場面
print デバッグ 気になる所に値を表示する行を足す 小さなプログラム・すぐ見たいとき
ログ ログレベル付きで記録し、後から読む 本番・たまにしか起きないバグ
デバッガー 実行を止めて変数・呼び出し元を見る 手元で再現できるバグ
ブレークポイント デバッガーで止める場所を決める 怪しい行の前後を見たいとき
ステップ実行 止めた所から1行ずつ進める 値が変わる瞬間を見たいとき
二分探索 コードや入力を半分ずつ外して絞る 原因の場所の見当がつかないとき
git bisect コミットの履歴を二分探索する 前は動いていた・いつ壊れたか不明
ラバーダック コードを1行ずつ声に出して説明する 読み違い・思い込みを見つけたいとき

ラバーダックデバッグは、机に置いたゴムのアヒルにコードを1行ずつ説明する方法です。GitHub のブログによると、1999年の書籍『The Pragmatic Programmer』で紹介され、声に出して説明することで自分のコードの理解が深まる、と説明されています。

デバッガーとログの使い分け

デバッガーが向いている

手元で毎回再現できる

どの変数が怪しいか分からない(全部見たい)

呼び出しの順番(コールスタック)を見たい

ログが向いている

本番でだけ・たまにしか起きない

止めると動きが変わる(並行処理・通信のタイムアウト)

あとから時系列で追いたい

flowchart TD
    A{手元で毎回<br>再現できる} -->|はい| B[デバッガーで止めて<br>変数を見る]
    A -->|いいえ| C[ログを足して<br>起きたときの値を残す]
    B --> D{前は動いていた}
    C --> D
    D -->|はい| E[git bisectで<br>壊れたコミットを探す]

デバッガーの基本用語(ブレークポイント・ステップ実行)

この節では、どのデバッガーにも出てくる言葉の意味が分かります。

デバッガーは、動いているプログラムを好きな所で止めて、そのときの変数の値や呼び出し元を見る道具です。 自動でバグを直すものではありません。

用語 意味
ブレークポイント 実行を止める行の目印
条件付きブレークポイント 式が真のときだけ止まる(例:name == "ぶどう")
ステップオーバー 今の行を実行して次の行へ。関数の中には入らない
ステップイン 今の行で呼んでいる関数の中に入る
ステップアウト 今の関数の最後まで実行し、呼び出し元に戻る
続行(continue) 次のブレークポイントまで進める
ウォッチ 指定した式の値を、止まるたびに表示する
コールスタック 今の関数を、どの関数がどの行から呼んだかの一覧
事後デバッグ(post-mortem) 例外で止まった直後の状態に入って調べる

ループの中の値を確かめたい

print デバッグ表示する行を足して実行し直す。見たい変数を変えるたびに書き直す。消し忘れに注意
→
デバッガーコードは変えずに止め、その場で好きな変数・式を見る。止まった所から1行ずつ進められる

どちらも正しい方法。小さく早く見るなら print、何が怪しいか分からないならデバッガー

例題:合計金額が合わないバグ(print デバッグとログ)

この節では、この記事で使う例題のバグと、print とログで値を見たときの出力が分かります。

りんご 120円×3、みかん 80円×5、ぶどう 400円×1 の合計を出すプログラムです。正しい合計は 360+400+400=1160 円ですが、400 円と表示されます。エラーは出ません(論理エラー)。

def calc_total(items):
    total = 0
    for name, price, qty in items:
        total = price * qty
    return total


items = [("りんご", 120, 3), ("みかん", 80, 5), ("ぶどう", 400, 1)]
print("合計:", calc_total(items))
合計: 400

print デバッグ

ループの中に、値を表示する1行を足しました。f"{name=}" は「変数名=値」の形で表示する書き方(Python 3.8 以降)です。

    for name, price, qty in items:
        total = price * qty
        print(f"[DEBUG] {name=} {price=} {qty=} {total=}")
[DEBUG] name='りんご' price=120 qty=3 total=360
[DEBUG] name='みかん' price=80 qty=5 total=400
[DEBUG] name='ぶどう' price=400 qty=1 total=400
合計: 400
回目 商品 期待する total 実際の total
1 りんご 360 360
2 みかん 760 400
3 ぶどう 1160 400

1回目は合っていて、2回目から足されずに上書きされています。 ここで「= で代入しているので、前の値が消えている」という仮説が立ちます。

ログで残す(logging)

print は確かめたら消す前提ですが、ログは残しておき、出す量をレベルで切り替えます。

import logging

logging.basicConfig(level=logging.DEBUG,
                    format="%(asctime)s %(levelname)s %(funcName)s:%(lineno)d %(message)s")
log = logging.getLogger(__name__)


def calc_total(items):
    total = 0
    for name, price, qty in items:
        total = price * qty
        log.debug("name=%s price=%d qty=%d total=%d", name, price, qty, total)
    return total


items = [("りんご", 120, 3), ("みかん", 80, 5), ("ぶどう", 400, 1)]
log.info("合計: %d", calc_total(items))
2026-10-23 17:02:58,887 DEBUG calc_total:12 name=りんご price=120 qty=3 total=360
2026-10-23 17:02:58,887 DEBUG calc_total:12 name=みかん price=80 qty=5 total=400
2026-10-23 17:02:58,887 DEBUG calc_total:12 name=ぶどう price=400 qty=1 total=400
2026-10-23 17:02:58,887 INFO <module>:17 合計: 400

level=logging.INFO に変えると、DEBUG の3行は出なくなり、INFO の1行だけになりました。コードを消さずに、出す量だけを変えられます。設定の書き方は、関連記事「Python loggingの使い方|用途別コピペ設定集と出ない原因」で扱っています。

Python(pdb)でデバッグする

この節では、Python に標準で付いているデバッガー pdb で、ブレークポイントを置いて変数を見る手順が分かります。

pdb は Python の標準ライブラリに入っているデバッガーで、追加のインストールは要りません。 始め方は3つあります。

始め方 書き方 止まる場所
コマンドで起動 python -m pdb cart.py 1行目
コードに書く 止めたい行の前に breakpoint() その行
例外の後で調べる python -m pdb -c c cart.py 例外が出た所(事後デバッグ)

ブレークポイントを置いて1行ずつ進める

python -m pdb cart.py で起動し、4行目にブレークポイントを置いて進めました。(Pdb) の後ろが入力したコマンドです(入力した文字は表示されないので、下のコメントで補っています)。

$ python3 -m pdb cart.py
> ~/demo/cart.py(1)<module>()
-> def calc_total(items):
(Pdb) b 4                       ← 4行目にブレークポイント
Breakpoint 1 at ~/demo/cart.py:4
(Pdb) c                         ← 続行(ブレークポイントまで進む)
> ~/demo/cart.py(4)calc_total()
-> total = price * qty
(Pdb) p name, price, qty, total ← 変数を表示
('りんご', 120, 3, 0)
(Pdb) display total             ← 止まるたびに total を表示
display total: 0
(Pdb) n                         ← 次の行へ
> ~/demo/cart.py(3)calc_total()
-> for name, price, qty in items:
display total: 360  [old: 0]
(Pdb) n
> ~/demo/cart.py(4)calc_total()
-> total = price * qty
(Pdb) n
> ~/demo/cart.py(3)calc_total()
-> for name, price, qty in items:
display total: 400  [old: 360]

display total: 400 [old: 360] が決め手です。 みかんの行を実行したら、360 に足されて 760 になるはずの total が、400 に置き換わりました。

条件付きブレークポイントとコールスタック

3つ目の商品(ぶどう)のときだけ止めたいときは、b 行番号, 条件 と書きます。

(Pdb) b 4, name == "ぶどう"
Breakpoint 1 at ~/demo/cart.py:4
(Pdb) c
> ~/demo/cart.py(4)calc_total()
-> total = price * qty
(Pdb) p name, total
('ぶどう', 400)
(Pdb) w                         ← コールスタック(呼び出し元の一覧)
  /usr/lib/python3.11/bdb.py(600)run()
-> exec(cmd, globals, locals)
  <string>(1)<module>()
  ~/demo/cart.py(9)<module>()
-> print("合計:", calc_total(items))
> ~/demo/cart.py(4)calc_total()
-> total = price * qty
(Pdb) ll                        ← 今の関数のソースを全部表示
  1     def calc_total(items):
  2         total = 0
  3         for name, price, qty in items:
  4 B->         total = price * qty
  5         return total

ll の B はブレークポイントのある行、-> は次に実行する行です。

breakpoint() をコードに書く

止めたい行の前に breakpoint() を書くと、そこで pdb が始まります(Python 3.7 以降)。

    for name, price, qty in items:
        breakpoint()
        total = price * qty
> ~/demo/cart_bp.py(5)calc_total()
-> total = price * qty
(Pdb) p name, total
('りんご', 0)
(Pdb) c
> ~/demo/cart_bp.py(5)calc_total()
-> total = price * qty
(Pdb) p name, total
('みかん', 360)

環境変数 PYTHONBREAKPOINT=0 を付けて動かすと、breakpoint() は何もしませんでした。 PYTHONBREAKPOINT=0 python3 cart_bp.py は止まらずに 合計: 400 を出して終わりました。ただし、breakpoint() の消し忘れを防ぐ方法ではないので、確かめ終わったら行を消します。

pdb の途中で q(終了)を入力すると、Python 3.11 では bdb.BdbQuit という例外で止まりました。プログラムの誤りではなく、デバッガーを抜けたときの表示です。

例外が出た所から調べる(事後デバッグ)

クラスごとの平均点を出すプログラムが、2組(点数が0件)で止まります。

def average(scores):
    return sum(scores) / len(scores)


def report(classes):
    for name, scores in classes.items():
        print(name, average(scores))


report({"1組": [70, 80, 90], "2組": []})

python3 -m pdb -c c avg.py で動かすと、例外が出た所で止まり、そのときの変数を見られます。u(up)で呼び出し元の関数に移れます。

ZeroDivisionError: division by zero
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
> ~/demo/avg.py(2)average()
-> return sum(scores) / len(scores)
(Pdb) p scores
[]
(Pdb) u
> ~/demo/avg.py(7)report()
-> print(name, average(scores))
(Pdb) p name, scores
('2組', [])

例外の行(2行目)は「0で割った」場所で、原因は「点数が0件のクラスを渡したこと」です。 直し方は、average で0件のときの扱いを決めるか、report で0件のクラスを飛ばすかの2通りがあり、仕様で決めます。

pdb のよく使うコマンド

コマンド 意味
b 4・b 4, 条件・b 関数名 ブレークポイントを置く(引数なしで一覧)
c 次のブレークポイントまで続行
n 次の行へ(関数の中に入らない)
s 関数の中に入る
r 今の関数が返るまで進める
p 式・pp 式 値を表示・見やすく表示
display 式 止まるたびに表示(変わったら old も出る)
l・ll ソースを表示・今の関数を全部表示
w・u・d コールスタック・上の関数へ・下の関数へ
cl 番号 ブレークポイントを消す
q 終了

公式ドキュメントでは、Python 3.14 で、動いているプロセスに後からつなぐ python -m pdb -p プロセスID が加わっています。

JavaScript(Node.js の inspect)でデバッグする

この節では、Node.js に付いているデバッガーで、同じバグを止めて変数を見る手順が分かります。

同じ例題を JavaScript で書きました。実行すると 合計: 400 です。

function calcTotal(items) {
  let total = 0;
  for (const [name, price, qty] of items) {
    total = price * qty;
  }
  return total;
}

const items = [["りんご", 120, 3], ["みかん", 80, 5], ["ぶどう", 400, 1]];
console.log("合計:", calcTotal(items));

node inspect ファイル名 で、コマンドで操作するデバッガーが起動し、最初の行で止まります。 --port で待ち受けのポートを変えられます。

$ node inspect --port=37400 cart.js
< Debugger listening on ws://127.0.0.1:37400/d1cb2cde-...
< Debugger attached.
debug> Break on start in cart.js:9
> 9 const items = [["りんご", 120, 3], ["みかん", 80, 5], ["ぶどう", 400, 1]];
debug> sb(4)                          ← 4行目にブレークポイント
debug> c
break in cart.js:4
  3   for (const [name, price, qty] of items) {
> 4     total = price * qty;
  5   }
debug> exec [name, price, qty, total] ← 式を評価して表示
[ 'りんご', 120, 3, 0 ]
debug> watch('total')                 ← 止まるたびに total を表示
debug> n
step in cart.js:3
Watchers:
  0: total = 360
debug> c
break in cart.js:4
Watchers:
  0: total = 360
debug> bt                             ← コールスタック
#0 calcTotal cart.js:4:4
#1 (anonymous) cart.js:10:19
...
debug> c
break in cart.js:4
Watchers:
  0: total = 400

みかんの行を通ったあと、total は 760 ではなく 400 になりました。 pdb と同じ結果です。let total = 0; の後ろを total += price * qty; に直すと、合計: 1160 になります。

条件付きのブレークポイントは、sb('ファイル名', 行, '条件') と書きます。

debug> sb('cart.js', 4, 'name === "ぶどう"')
debug> c
break in cart.js:4
debug> exec [name, total]
[ 'ぶどう', 400 ]
コマンド 意味
sb(行)・sb('ファイル', 行, '条件') ブレークポイントを置く
cb('ファイル', 行) ブレークポイントを消す
c・n・s・o 続行・次の行・中に入る・外に出る
exec 式・p 式 式を評価して表示
watch('式')・watchers ウォッチに足す・一覧
bt・list(5) コールスタック・前後5行のソース
repl その場で JavaScript を打てるモード(Ctrl+C で戻る)
restart・kill 最初から・止める

コードの中に debugger; と書いた行もブレークポイントになります。公式ドキュメントでは、環境変数 NODE_INSPECT_RESUME_ON_START=1 を付けると、最初の行で止まらずに最初のブレークポイントまで進みます。

Chrome の DevTools でつなぐ(--inspect)

node --inspect で起動すると、Chrome の DevTools や VS Code からつなげる待ち受けが開きます。

$ node --inspect=127.0.0.1:37402 cart.js
Debugger listening on ws://127.0.0.1:37402/2af8a7aa-...
For help, see: https://nodejs.org/en/docs/inspector
合計: 400

--inspect はつながる前にコードが進むので、最初から止めたいときは --inspect-brk(つながったら1行目で止まる)を使います。ブラウザで動く JavaScript のデバッグ(Sources パネル・ブレークポイント・Console)は、関連記事「Chrome DevToolsの使い方|デバッグ手順と便利機能一覧」で扱っています。

Java(jdb)でデバッグする

この節では、JDK に付いているデバッガー jdb で、ブレークポイントと変数の表示をする手順と、javac -g が要る理由が分かります。

public class Cart {
    static int calcTotal(String[] names, int[] prices, int[] qtys) {
        int total = 0;
        for (int i = 0; i < names.length; i++) {
            total = prices[i] * qtys[i];
        }
        return total;
    }

    public static void main(String[] args) {
        String[] names = {"りんご", "みかん", "ぶどう"};
        int[] prices = {120, 80, 400};
        int[] qtys = {3, 5, 1};
        System.out.println("合計: " + calcTotal(names, prices, qtys));
    }
}

javac -g でコンパイルし、jdb クラス名 で起動して、stop at クラス:行 でブレークポイントを置きます。

$ javac -g Cart.java
$ jdb Cart
Initializing jdb ...
> stop at Cart:5
Deferring breakpoint Cart:5.
It will be set after the class is loaded.
> run
VM Started: Set deferred breakpoint Cart:5

Breakpoint hit: "thread=main", Cart.calcTotal(), line=5 bci=12
5                total = prices[i] * qtys[i];

main[1] locals
Method arguments:
names = instance of java.lang.String[3] (id=443)
prices = instance of int[3] (id=444)
qtys = instance of int[3] (id=445)
Local variables:
total = 0
i = 0
main[1] next
Step completed: "thread=main", Cart.calcTotal(), line=4 bci=22
main[1] next
Breakpoint hit: "thread=main", Cart.calcTotal(), line=5 bci=12
main[1] print total
 total = 360
main[1] cont
Breakpoint hit: "thread=main", Cart.calcTotal(), line=5 bci=12
main[1] print names[i]
 names[i] = "ぶどう"
main[1] print total
 total = 400
main[1] where
  [1] Cart.calcTotal (Cart.java:5)
  [2] Cart.main (Cart.java:14)

ぶどうの行に来たとき、total は 760 ではなく 400 でした。 Python・JavaScript と同じく、代入で前の値が消えています。

-g を付けないと変数が見えない

-g を付けずにコンパイルしたクラスで同じことをすると、ローカル変数を表示できませんでした。

main[1] locals
Local variable information not available.  Compile with -g to generate variable information
main[1] print total
com.sun.tools.example.debug.expr.ParseException: Name unknown: total
 total = null

-g は、ローカル変数の名前などのデバッグ情報をクラスファイルに入れるオプションです。-g なしでも行番号は入るため、ブレークポイントでは止まりました。

例外が出た所で止める(catch)

$ java Avg
80
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at Avg.average(Avg.java:5)
    at Avg.main(Avg.java:10)

catch 例外クラス で、その例外が出た瞬間に止まります。

> catch java.lang.ArithmeticException
> run
Exception occurred: java.lang.ArithmeticException (uncaught)"thread=main", Avg.average(), line=5 bci=36
5            return sum / scores.length;

main[1] where
  [1] Avg.average (Avg.java:5)
  [2] Avg.main (Avg.java:10)
main[1] locals
Method arguments:
scores = instance of int[0] (id=454)
Local variables:
sum = 0

int[0](要素0個の配列)が渡されたことが、止まった時点の変数から分かります。

コマンド 意味(jdb の help より)
stop at クラス:行・stop in クラス.メソッド ブレークポイントを置く
clear クラス:行・clear 消す・一覧
run・cont 実行を始める・続行
next・step・step up 次の行・中に入る・呼び出し元に戻るまで
locals・print 式・dump 式 ローカル変数・式の値・オブジェクトの中身
where・up・down コールスタック・上へ・下へ
catch 例外クラス 例外が出たら止める
set 変数 = 値 値を書き換える

ほかの言語のデバッガー

C 言語では GDB を使います。同じ例題を gcc -g -O0 でコンパイルし、GDB 15.1 で6行目に止めました。

$ gdb -q -batch -ex 'break 6' -ex run -ex 'info locals' -ex continue -ex 'print total' ./cart_c
Breakpoint 1 at 0x118c: file cart.c, line 6.

Breakpoint 1, calc_total (prices=0x7fffffff8660, qtys=0x7fffffff866c, n=3) at cart.c:6
6           total = prices[i] * qtys[i];
i = 0
total = 0

Breakpoint 1, calc_total (prices=0x7fffffff8660, qtys=0x7fffffff866c, n=3) at cart.c:6
6           total = prices[i] * qtys[i];
$1 = 360
言語 主なデバッガー
Python pdb(標準)、VS Code の Python Debugger(debugpy)
JavaScript node inspect、Chrome DevTools、VS Code(組み込み)
Java jdb(JDK に付属)、IDE のデバッガー
C・C++ GDB、LLDB
Go Delve(dlv)

3つのデバッガーのコマンド対応表

操作 pdb node inspect jdb
止める場所 b 4 sb(4) stop at Cart:5
続行 c c cont
次の行 n n next
中に入る s s step
値を見る p total exec total print total
呼び出し元 w bt where

VS Codeのデバッガーの使い方

この節では、VS Code でブレークポイントを置き、ボタンとキーで1行ずつ進める方法が分かります。

行番号の左をクリックするか F9 でブレークポイントを置き、F5 でデバッグを始めます。 公式ドキュメント(2026年10月21日更新)による手順です。

1

デバッガーの拡張機能を確かめる

JavaScript・TypeScript・Node.js は組み込み。Python は Python Debugger(ms-python.debugpy。Python 拡張機能と一緒に入る)、Java は Debugger for Java(vscjava.vscode-java-debug)。

準備

2

ブレークポイントを置く

止めたい行の行番号の左(余白)をクリックするか F9。赤い丸が付く。右クリックで条件付き・ログポイントも選べる。

F9

3

デバッグを始める

F5、または「実行とデバッグ」ビュー(Ctrl+Shift+D、Mac は ⇧⌘D)の「Run and Debug」ボタン。

F5

4

止まったら値を見る

左の VARIABLES(変数)・WATCH(ウォッチ)・CALL STACK(コールスタック)で見る。デバッグ コンソールでは、その場で式を評価できる。

確認

5

進める

上に出るツールバーか、下の表のキーで1行ずつ進める。

F10・F11

操作 Windows・Linux Mac
続行・一時停止(Continue / Pause) F5 F5
ステップオーバー(Step Over) F10 F10
ステップイン(Step Into) F11 F11
ステップアウト(Step Out) Shift+F11 ⇧F11
再起動(Restart) Ctrl+Shift+F5 ⇧⌘F5
停止(Stop) Shift+F5 ⇧F5
デバッグ コンソールを開く Ctrl+Shift+Y ⇧⌘Y

公式ドキュメントでは、次のブレークポイントが使えます(ログポイントなどは、言語のデバッガー拡張機能が対応している場合)。

種類 何ができるか
条件付き 式が真のとき・指定回数に達したときだけ止まる
トリガー 別のブレークポイントで止まった後にだけ有効になる
インライン 1行の中の特定の列で止まる(⇧F9)
関数 指定した名前の関数に入ったら止まる
データ 変数が変わった・読まれたときに止まる
ログポイント 止まらずに、メッセージをデバッグ コンソールに出す

ログポイントを使うと、print の行をコードに足さずに値を出せます。 消し忘れも起きません。

launch.json(起動の設定)

引数や環境変数を渡したいときは、.vscode/launch.json に設定を書きます。「実行とデバッグ」ビューの「create a launch.json file」から作れます。type・request・name の3つは必ず書きます。公式ドキュメントの例です。

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch Program",
      "skipFiles": ["<node_internals>/**"],
      "program": "${workspaceFolder}/app.js"
    }
  ]
}

Python の設定は "type": "debugpy" です("python" は非推奨と公式ドキュメントに書かれています)。

{
  "name": "Python Debugger: Current File (Integrated Terminal)",
  "type": "debugpy",
  "request": "launch",
  "program": "${file}",
  "console": "integratedTerminal"
}

Java は "type": "java" で、mainClass に起動するクラスを書きます。${file}(開いているファイル)・${workspaceFolder}(ワークスペースのフォルダー)のような変数で、パスを直接書かずに済みます。"request": "attach" にすると、すでに動いているプログラムにつなぎます。

エラーメッセージ(スタックトレース)の読み方

この節では、エラーが出たとき、どの行から読めば原因に早くたどり着けるかが分かります。

最初に読むのは「例外の種類とメッセージ」の行、次に「自分が書いたファイルの行」です。 その位置が、言語によって上と下で逆になります。

言語 例外とメッセージの位置 自分のコードの行の探し方
Python いちばん下 下から上へ。いちばん下の File 行が止まった所
Java いちばん上 上から下へ。at 自分のクラス の最初の行
Node.js 上(コードの抜粋の下) 上から下へ。node:internal の行は飛ばす

Node.js で null のプロパティを読んだときの出力です。

function getName(user) {
  return user.profile.name.toUpperCase();
}
const users = [{ profile: { name: "sato" } }, { profile: null }];
users.forEach((u) => console.log(getName(u)));
SATO
~/demo/err.js:2
  return user.profile.name.toUpperCase();
                      ^

TypeError: Cannot read properties of null (reading 'name')
    at getName (~/demo/err.js:2:23)
    at ~/demo/err.js:5:34
    at Array.forEach (<anonymous>)
    at Object.<anonymous> (~/demo/err.js:5:7)
    at Module._compile (node:internal/modules/cjs/loader:1705:14)
    ...

Node.js v22.22.2
  • 1例外の種類を読むTypeError(型の誤り)・ZeroDivisionError(0で割った)など、何が起きたかの分類
  • 2メッセージを読むreading 'name' は「null の name を読もうとした」。読まれた側(user.profile)が null
  • 3自分のファイルの行を開くerr.js:2:23 は2行目の23文字目。^ の印も同じ場所を指す
  • 4呼び出し元をたどる5行目の forEach から、2人目のユーザーで呼ばれた。原因はデータの側にある
  • 5メッセージそのままで検索するファイル名・行番号・自分の変数名を除いた部分で検索すると、同じ例を見つけやすい

Python 3.11 では、^^^^^ の印で、行の中のどの式で例外が出たかも示されます。前の節の ZeroDivisionError では、sum(scores) / len(scores) の / の所に ^ が付きました。Java の Caused by の読み方は、関連記事「Javaスタックトレースの読み方|Caused byの意味と原因の探し方」で扱っています。

いつ壊れたかを探す(git bisect で二分探索)

この節では、「前は動いていたのに、いつの間にか壊れた」ときに、原因のコミットを自動で探す方法が分かります。

git bisect は、コミットの履歴を二分探索して、バグが入った最初のコミットを探すコマンドです。 「正しい(good)」と「おかしい(bad)」のあいだの真ん中のコミットを取り出し、確かめた結果で範囲を半分にしていきます。

例題の合計の関数を v1.0(正しい)で作り、その後に15個のコミットを足したリポジトリで試しました。どこかで += が = に変わっています。合計が 1160 か確かめるテストを用意します。

# test_cart.py
from cart import calc_total
assert calc_total([("りんご", 120, 3), ("みかん", 80, 5), ("ぶどう", 400, 1)]) == 1160
print("OK")
git bisect start HEAD v1.0          # おかしい HEAD と、正しい v1.0
git bisect run sh -c 'python3 test_cart.py 2>/dev/null'
git bisect reset                    # 元のブランチに戻る
Bisecting: 7 revisions left to test after this (roughly 3 steps)
[c96a41962f9ec6c98d4e18cd06458a7f05825eaa] メモを更新 7
running 'sh' '-c' 'python3 test_cart.py 2>/dev/null'
OK
Bisecting: 3 revisions left to test after this (roughly 2 steps)
[b495175f5a922384d66eea629cc8ab8e64ab8572] メモを更新 11
running 'sh' '-c' 'python3 test_cart.py 2>/dev/null'
Bisecting: 1 revision left to test after this (roughly 1 step)
[cb17b469c94569c0ce28e9efd0aaac6c5168ed30] 合計の計算を整理
running 'sh' '-c' 'python3 test_cart.py 2>/dev/null'
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[57824a51568044c7d4e79241a745d47fe8cf6cb1] メモを更新 8
running 'sh' '-c' 'python3 test_cart.py 2>/dev/null'
OK
cb17b469c94569c0ce28e9efd0aaac6c5168ed30 is the first bad commit
commit cb17b469c94569c0ce28e9efd0aaac6c5168ed30
Author: demo <demo@example.com>
Date:   Fri Oct 23 17:02:42 2026 +0900

    合計の計算を整理

 cart.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
bisect found first bad commit
16個調べたコミットv1.0 と、その後の15個
4回テストを動かした回数半分ずつ範囲を絞った
1行見つかった変更+= が = に変わっていた

見つかったコミットの差分です。

     for name, price, qty in items:
-        total += price * qty
+        total = price * qty
     return total

git bisect run に渡すコマンドは、正しいとき終了コード 0、おかしいとき 1〜127(125 を除く)で終わるようにします。公式ドキュメントでは、125 は「このコミットは確かめられないので飛ばす」の意味です。対話で1つずつ good・bad を伝える使い方は、関連記事「git bisectの使い方|実行ログで学ぶバグ混入コミットの特定」で扱っています。

直して確かめる

原因の行を total += price * qty に直し、同じテストを流しました。

$ python3 cart.py
合計: 1160
$ python3 test_cart.py
OK

このテストを残しておくと、同じバグがもう一度入ったときに、すぐに気づけます。 手順5「確かめる」を、次からは自動で行えるようになります。

デバッグでよくある失敗と対策

この節では、デバッグに時間がかかる原因になりやすい失敗と、その避け方が分かります。

よくある失敗 何が起きるか 対策
再現する前に直し始める 直ったのか、たまたま起きなかったのか分からない 再現手順を先に書く
症状が出た行だけを直す 原因が残り、別の場所でまた起きる 値がずれ始めた場所までさかのぼる
仮説を一度にいくつも試す どの変更で直ったのか分からない 1回に1つだけ変えて確かめる
print・breakpoint() を消し忘れる 本番でログがあふれる・処理が止まる ログポイント・ログレベルを使う。差分を見て消す
エラー文の最初の数語しか読まない 原因の場所を見落とす 例外の種類・メッセージ・自分の行の順に読む
-g なしで Java をデバッグする 変数の名前が出ない javac -g でコンパイルする
本番でデバッグモードのまま公開する 内部の情報が外から見える 公開前に設定を戻す
直した後にテストを流さない デグレードに気づかない 再現手順をテストにして毎回流す
  • 1再現手順・期待した結果・実際の結果を書いた人に聞くとき・報告するときにもそのまま使える
  • 2どこまで正しいかを確かめた例題では「1回目は正しく、2回目からずれる」
  • 3原因を1文で説明できる例題では「= で代入しているので前の合計が消える」
  • 4再現手順でバグが消えた合計が 1160 になった
  • 5デバッグ用の行を消したprint・breakpoint()・debugger; が残っていない

エラー文やスタックトレースを生成AIに渡して原因を聞くときの書き方は、関連記事「エラーを生成AIに聞くプロンプトの書き方|スタックトレースの渡し方」で扱っています。

よくある質問

Q. デバッグとは簡単に言うと何ですか? A. プログラムの誤り(バグ)の原因を見つけて直す作業です。「再現→切り分け→原因→修正→確認」の順に進めます。

Q. 「デバッグ」と「デバック」はどちらが正しい? A. 「デバッグ」が正しい書き方です。英語の debug(de+bug)から来ていて、最後は g の音です。

Q. デバッグとテストの違いは? A. テストは不具合があるかを見つける作業、デバッグは不具合の原因を突き止めて直す作業です。失敗したテストは、デバッグの再現手順として使えます。

Q. デバッガーとは? A. プログラムを好きな所で止めて、変数の値や呼び出し元を見る道具です。Python の pdb、Node.js の inspect、Java の jdb、C の GDB、VS Code のデバッガーなどがあります。ゲーム開発では、遊んで不具合を探す担当者をデバッガーと呼ぶこともあります。

Q. ブレークポイントとは? A. デバッガーで実行を止める行の目印です。条件を付けて、特定の値のときだけ止めることもできます。

Q. ステップイン・ステップオーバー・ステップアウトの違いは? A. ステップインは呼んでいる関数の中に入る、ステップオーバーは関数の中に入らずに次の行へ進む、ステップアウトは今の関数の最後まで進めて呼び出し元に戻る操作です。VS Code では F11・F10・Shift+F11 です。

Q. print デバッグは使わないほうがよいですか? A. 小さなプログラムで値を早く見たいときには、手軽で正しい方法です。見たい値が決まっていないとき、ループの途中を何度も見たいときはデバッガーのほうが早く、消し忘れが心配ならログやログポイントを使います。

Q. 再現しないバグはどうやってデバッグしますか? A. 起きたときの入力・時刻・値をログに残し、起きたときと起きないときの違い(データ・設定・版・タイミング)を1つずつ比べます。止めると動きが変わるバグは、デバッガーよりログが向いています。

Q. USB デバッグとは? A. Android の「開発者向けオプション」にある設定で、有効にすると、開発用の PC と Android Debug Bridge(adb)でつながり、アプリを動かしてデバッグできます。Android の公式ドキュメントでは、設定の「ビルド番号」を7回タップすると開発者向けオプションが表示されます。

Q. デバッグモードとは? A. アプリやフレームワークで、詳しいエラーやログを出すようにする動かし方です。たとえば WordPress の WP_DEBUG は既定が false で、公式ドキュメントは公開中のサイトで使うことをすすめていません。

まとめ

  • デバッグとは、バグの原因を突き止めて直す作業。bug(虫)+de(取り除く)で、書き方は「デバッグ」。
  • テストは「見つける」、デバッグは「原因を直す」。失敗したテストが再現手順になる。
  • 手順は、再現→切り分け→原因→修正→確認。原因は1文で説明できるまで確かめる。
  • print は手軽、ログは本番やたまにしか起きないバグ、デバッガーは手元で再現できるバグに向く。
  • pdb・node inspect・jdb のどれでも、ブレークポイントで止めて total が 360 から 400 に置き換わる瞬間を見られた。Java は javac -g が要る。
  • エラー文は、例外の種類・メッセージ・自分のファイルの行の順に読む。Python は下から、Java と Node.js は上から。
  • いつ壊れたか分からないときは git bisect。16個のコミットから4回のテストで原因のコミットが見つかった。

参考・出典

確認日はいずれも 2026年10月23日です。

  • NTT東日本 BizDrive「デバッグ」:https://business.ntt-east.co.jp/bizdrive/word/debug.html
  • Smithsonian Institution「Log Book With Computer Bug」:https://www.si.edu/object/log-book-computer-bug:nmah_334663
  • GitHub ブログ「おもちゃのアヒルとプログラミングにどんな関係があるのでしょうか?」:https://github.blog/jp/2024-10-23-whats-with-all-the-ducks/
  • Python ドキュメント「pdb --- The Python Debugger」:https://docs.python.org/3/library/pdb.html
  • Node.js ドキュメント「Debugger」:https://nodejs.org/api/debugger.html
  • Oracle「The jdb Command(JDK 21)」:https://docs.oracle.com/en/java/javase/21/docs/specs/man/jdb.html
  • Visual Studio Code「Debug code with Visual Studio Code」:https://code.visualstudio.com/docs/debugtest/debugging
  • Visual Studio Code「Configure debugging」:https://code.visualstudio.com/docs/debugtest/debugging-configuration
  • Visual Studio Code「Python debugging in VS Code」:https://code.visualstudio.com/docs/python/debugging
  • Visual Studio Code「Running and debugging Java」:https://code.visualstudio.com/docs/java/java-debugging
  • GitHub「microsoft/vscode-java-debug Configuration.md」:https://github.com/microsoft/vscode-java-debug/blob/main/Configuration.md
  • Git「git-bisect Documentation」:https://git-scm.com/docs/git-bisect
  • Android Developers「Configure on-device developer options」:https://developer.android.com/studio/debug/dev-options
  • WordPress Developer Resources「Debugging in WordPress」:https://developer.wordpress.org/advanced-administration/debug/debug-wordpress/

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「デバッグとは|意味・手順・方法をPython・JS・Javaの実例で解説」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?