日本ソフトウェア科学会第 35 回大会 (2018 年度) 講演論文集

プログラムを停止させないデバッグを可能とする手法
の提案
丹野 治門

岩崎 英哉

GUI プログラム，ゲームプログラム，ネットワークプログラム，センサ情報処理プログラムのように，双方向性や実時間性が高いプログラムにおいては，ブレークポイントを用いてプログラムを一時停止させるような従来型のデバッ
グ手法は適していない．その理由の第一は，これらのプログラムにおいては，ユーザ操作やセンサ入力のタイミング
や順番が重要であり，デバッグのためにプログラムの実行を途中で中断させると，プログラムが期待通りの動きをしなくなってしまうためである．第二の理由は，プログラムの内部状態を確認するたびにプログラムを停止させていては，デバッグの効率が著しく悪くなるためである．以上の問題点を解決するため，本研究ではプログラムの実行位置の情報と変数情報を実時間に出力して表示するデバッグの手法を提案し，C# プログラムのデバッガとして実装した．
本デバッグ手法を用いることにより，開発者はプログラムを停止させずに効率よくデバッグを行えるようになる．
Programs such as GUI programs, game programs, network programs, and sensor information processing programs are highly interactive and real-time. For such programs, traditional debugging methods that suspend
a program using breakpoints are not appropriate for the following two reasons. First, the timing and orderof user operations or sensor inputs are very important in these programs. If an execution of a program issuspended by a debugger, it might not work as expected. The second reason is that the debugging is awfully
ineﬃcient if the program is suspended each time the developer wants to observe the states of internal data
structures. To solve these problems, we propose a debugging method for displaying information on executed
paths and variables of the program in real time. We implemented the proposed method as a debugger for
C #. By using our debugging method, the developer can debug highly interactive and real-time programs
eﬃciently without suspending their executions.

グラムを一時停止させるような従来型のデバッグ手法

1 はじめには適していない．その理由の第一は，これらのアプリ

GUI プログラム，ゲームプログラム，ネットワー

ケーションにおいては，ユーザ操作やセンサ入力のタ

クプログラム，センサ情報処理プログラムのように，イミングや順番が重要であり，これを途中で中断させ双方向性や実時間性が高いプログラムは以下の特質

ると，プログラムが期待通りの動きをしなくなってしを持っている．

まうためである．第二の理由は，プログラムの内部状• ユーザ操作やセンサ入力等のイベントの発生タ
イミングや順番が重要である．

態を確認しようとするたびにプログラムを停止させていては，デバッグの効率が悪くなるためである．

• プログラムへの入力と，入力を受け取ったとき

例えば，アクションゲームのプログラムにおいて，のプログラムの状態の様々な組み合わせにより，プレイヤが敵キャラクタへ攻撃を行うロジックをデプログラムの挙動が複雑に変化する．

バッグすることを考える．C# で記述されたデバッグ

これらの特質によりブレークポイントを用いてプロ

対象のコードをソースコード 1 に示す．このコード∗ Debugging Method without Suspending Program

は，プレイヤの入力を元に，プレイヤが攻撃ボタンをThis is an unrefereed paper. Copyrights belong to
the Authors.
Haruto Tanno, Hideya Iwasaki, 電気通信大学大学院,
The University of Electro-Communications.

押したときに，プレイヤの近くに存在する敵キャラク
タの体力を減らし，敵キャラクタの体力が 0 になるとその敵キャラクタを消滅させるという処理を行う．こソースコード 1
1

デバッグ対象のコード例

public void PlayerAttack(PlayerInput input, Player player, List<Enemy> enemyList)

2 {4

int damagePoint = player.OffensivePower;
if (input.AttackButton)

5

{

3

7

if (input.DashButton) damagePoint *= 5; //プレイヤがダッシュしていたらダメージ 5倍
foreach (var enemy in enemyList)

8

{

6

9

if (Vector3.Distance(player.Position, enemy.Position) <= 5.0)

10{//敵がプレイヤの近距離に存在する

11

if (!enemy.IsInvincible)

12{//敵が無敵状態ではない

enemy.HitPoint -= damagePoint;//敵のHP を減らす
if (enemy.HitPoint <= 0) enemy.Dead();//敵のHP が 0 以下なら消滅

13
14
15

}

16

}

enemy.EndPlayerCollision();

17
18
19

}
}

20 }

の処理は一定間隔 (例えば 1 秒間に 30 回) で実時間にントの処理，ネットワークプログラムやセンサ情報処

行われ，ユーザの入力によりその挙動が変わるため双理プログラムにおいて，実時間に送受信されるデータ方向性も高い．このコードにおいて，プレイヤがダッを扱う処理といった双方向性や実時間性の高いプログ

シュボタンを押し，5 倍の攻撃力で近距離の敵キャラ

ラムをデバッグする際に共通する問題である．

クタを攻撃すると 2-3 回で倒せるはずなのに，何らか

このような問題を解決するため，本研究ではプログのバグで倒すことができず，そのバグの原因を特定しラムを停止させることなくプログラムの実行経路情

たいとする．バグの原因は「プレイヤはボタンを押報と変数情報を，実時間で出力し可視化するデバッグ

したが input.AttackButton が真になっていない」，の手法を提案し，C# プログラムのデバッガとして実「 input.DashButton が真になっておらずダメージが装した．本デバッグ手法を用いることにより，開発者

5 倍になっていない」「，EnemyList に攻撃対象の敵キャ

は実時間性や双方向性の高いソースコード 1 に示しラクタが含まれていない」，「Vector3 の Distance のたようなプログラムを，効率よくデバッグすることが計算結果が正しくない」，「enemy.IsInvincible が可能となる．

(想定と異なり) 真になっている」など複数考えられ

本論文の貢献は以下である．

る．プレイヤの様々な操作に対し，プログラムがどの• 手続き型言語やオブジェクト指向言語で記述さ位置まで実行されたか，またそのときの各変数の値がれた双方向性，実時間性の高いプログラム一般にどのようになっているかを確認するため，様々な箇所

対して広く適用できるデバッグ手法を提案してへブレークポイントを設定すると，何らかのボタンをいる．

押す，敵キャラクタの近距離に移動する，といった動• C# プログラムを対象として，提案するデバッ

作のたびにプログラムが中断されてしまいデバッグ

グ手法を実現する方法を具体的に示している．この効率が悪くなる．また，例えば，AttackButton をの方法は C# 以外の多くの言語に対しても適応まず押し，次に DashButton を押すといった操作が，できる．

操作の途中でプログラムが中断されることにより正

著者は過去にゲームプログラムに特化した，プログラ

常に行えなくなる．これは，例えば GUI プログラム

ムを停止させずにデバッグを行える手法 [20] を提案

における画面上の要素の操作に対して発生するイベ

しており，本論文の内容はこれを発展させたものである．著者の過去の手法はゲームシステム記述用のドメ行い，出力として計算結果の表示や，データの蓄積，イン特化言語を対象としている上，実用上足りない機

更新う．GUI プログラム，ゲームプログラム，ネッ

能が多くあった．本研究ではオブジェクト指向言語でトワークプログラム，センサ情報処理プログラムなど記述された双方向性や実時間性の高いプログラム一では，このような部分がプログラムの実行時間の大半

般に対して広く適用できるデバッグ手法を提案していを占める上，ユーザ操作や，センサ，通信装置からのる．過去の手法に対し，提案手法では実用上有用な特

様々な入力パターンによりプログラムの内部状態が徴として主に，(1) ソースコードファイル，メソッド，

刻々と変化するため，その挙動は予測しにくくデバッ

ステートメントという 3 段階の粒度で実行された部

グにも労力がかかる．

位を実時間に可視化することで確認したい箇所を徐々

バグにはいくつかの種類がある．性能に関するバグに絞り込める機能，(2) ループ処理 (for 文，foreachの原因特定にはプロファイラが活用できる．機能に関文) 等によりプログラム中で複数実行された実行経路

するバグには，実行時エラーと論理エラーの 2 種類がについて，条件を指定することで，開発者が着目したある．実行時エラーはプログラムが動作し続けられない実行経路や変数の値の情報のみを実時間に可視化くなるエラーであり，NULL 値の予期しない参照等できる機能，(3) マルチスレッドを用いたプログラムにより発生する．一方，論理エラーは意図した仕様とへの対応，の 3 つをもつ．

異なる動きをするものであり，計算ロジックに誤りが

また，C# における本実装方式では，デバッグ対象ある場合などに発生する．実時間性や双方向性の高いの C# コードを自動変換し，対象コードのステート処理部分で発生する論理エラーは，既存のデバッガのメントごとにデバッグ情報を取得するためのコード

ブレークポイントやステップ実行を用いることでは，を埋め込むことで実時間にデバッグ用の情報を取得

取り除くことが難しかったり，大きな労力がかかった

する．提案手法を適用するにあたっては，ユーザはデりする．本研究では，このような論理エラーをデバッ

バッグ対象のソースコードに対して一切変更を加えグの対象とする．

る必要はなく，変換後のコードを動作させるためのデ以上をまとめると，表 1 のようになる．表 1(a) がバッグコード実行用のライブラリをあらかじめ対象

本研究の対象領域である．表 1(b) のような非実時間，

コードへリンクしておくだけでよい．

非双方向であるバッチ処理の部分のデバッグは既存の以降，本稿では 2 節で，本研究が対象とするスコー

デバッガでステップ実行を用いてプログラム実行時のプを明確にした上で，既存のデバッグ手法における問情報を調べる方法が有効である．また，表 1(c) につ

題点を述べる．次に 3 節で提案するデバッグ手法のいては，既存のデバッガを用いて，実行時エラーが発コンセプトとその特徴について述べ，4 節で提案手法生した際に実行位置や変数位置などを取得してバグの C# における本実装方式について説明する．5 節

原因を調べることが可能である．次節では，表 1(a)で提案手法のケーススタディを紹介し，6 節でオーバ

へ既存のデバッグ手法を適用したときの問題点についヘッド計測の結果について述べる． 最後に 7 節で今て説明する．

後の課題と本論文のまとめを述べる．

2. 2 既存手法の問題点

2 双方向性や実時間性が高いプログラムのデバッグ
2. 1 本研究の対象領域

本研究の対象である表 1(a) へ既存のデバッグ手法を適用したときの問題点について述べる．
古典的なデバッグの方法としては，デバッグ情報取本研究では，図 1 のように動作するプログラムに

得・表示用のコードをデバッグ対象のコードへ埋め込

おける，図 1(2) の部分をデバッグ対象とする．このむ方法 (以降，「printf デバッグ」と呼ぶ)，ブレーク部分では，入力としてユーザ入力，センサ情報，通信

ポイントを用いる方法 (Microsoft Visual Studio De-

データ等を受け取り，それらに対して何らかの処理を表1

本研究の対象領域

双方向性，実時間性が高い処理(図 1(2))
機能

論理エラー
実行時エラー

性能

バッチ処理(図 1(1),(3))(a) 本研究の対象領域(b) 既存デバッガが有用(c) 既存デバッガが有用(d) プロファイラが有用

動的に埋め込むことが可能な手法 [17] も存在するが，(1)ȅ‫̅܆‬ąЇ

いずれにせよデバッグ出力用のコードや Assertion を記述するのは開発者の負担が大きい．プロファイラはąȅ(2)ง༈ࠈąЇ(༄)ąȅ༏ࠅ༏
༄

ソースコードの各メソッドや各行に費やされた CPU
時間を，プログラムを実行し続けながら計測し，そのąȅ結果をソースコードに重畳表示するツールであり，パ(༄) ਈଇഇ‫ࠈ܆‬इЈਇ༏؆Ԇ

フォーマンス改善に主に用いられる．プロファイラを(3)ഇคąЇ用いると，コードのどの位置が実行されたかはわかる
が，どのような入力のタイミングで，どの箇所が実行

図1

双方向性や実時間性が高いプログラムの動きされたか，またそのときの変数の値は何であったかといった情報は得られない．

bugger†1 ，GDB†2 等)，Assertion をコードへ記述すより発展したデバッグ手法として，プログラムのロる方法 [14]，プロファイラ (Microsoft Visual Studio
Profiler†3 等) を用いる方法などがある [16]．printf

グを自動的に取得し，そのログに基づきデバッグを支

デバッグや，ブレークポイントを用いる方法は，実ムの実行経路情報を記録し，プログラムを実行させ際に多くの開発者に使われている [5]．しかしながら，たあとにその情報を視覚的に表示している．JIVE [6]ブレークポイント用いた方法はプログラムを停止さはプログラム実行ログからオブジェクト図，シーケンせる必要がある．ブレークポイント指定をプログラマ

ス図を生成し，更にクエリによって実行ログの中からブルに行える手法 [18]，自動で行う手法 [19]，行やス

確認した箇所を絞り込むことも可能である．Herman

テートメントではなくオブジェクトの状態に着目してらの手法 [1] では，表 1(a) の領域を記述する際に用い指定できる手法 [13] など，デバッガ利用者の利便性られるリアクティブプログラム (RP) を対象として，をより向上させた手法もあるが，プログラムを停止

デバッグ対象のプログラムが用いているライブラリへさせる方法では 1 節で説明したような問題点がある．

パッチをあてることで可視化のためのログを取得し，printf デバッグや Assertion を記述する方法を用いる

そのログからデータフローと処理のタイミングを可と，プログラムを停止させずにプログラムの内部状

視化するデバッガを提案している．また，自動収集し態を確認，監視することが可能ではあるが，プログラ

たプログラムのログを元に，開発者と対話しながらムの随所にデバッグ用のコードを記述しなければな

バグの箇所を絞り込む手法 [12] もある．これらの手らず，プログラムの保守性や可読性が下がる．アスペ法は表 1(a) の領域に対して，プログラムを停止させ

クト指向の考え方に基づき，デバッグ用コードを分離

ず，自動でログを集め，後からそのログを確認できる

して記述する手法 [11]，分離して記述したコードをため有用である．しかしながら，これらのアプローチ†1 https://msdn.microsoft.com/ja-jp/library/windows/desktop/sc65sadd†2 https://www.gnu.org/software/gdb/†3 https://docs.microsoft.com/ja-jp/visualstudio/profiling/

援するアプローチがある．ETV [15] では，プログラ

では，ログの確認はプログラムの実行後になるため，
入力に対するプログラムの実行経路や変数の情報を即時に確認し，その情報に基づき次の入力を考えて与える，といったような試行錯誤を効率よく行うこと

ができない．加えて，ログを可視化するアプローチで介したソースコード 1 のデバッグを例にして，提案は，プログラム実行時の全ての情報を記録することがするデバッグ手法の特徴について図 2 に基づいて説

現実的には難しいため，後からログを見ても必要な情明する．

報が確認できない場合もある．

(1) ソースコードファイルハイライト表示：

プログラムに対するあらゆる入力を記録しておき，

デバッグ対象のプログラムのソースコードファイル

プログラムの実行を再現させるアプローチもあり，C一覧を表示しており，プログラムが実行したメソッ

言語，Java，.NET，JavaScript /Node.js など様々なドを含むファイルを実時間にハイライト表示する (ハ言語，実行環境において実装が行われている [2–4,10]．

イライト表示はしばらくすると消える)．これによ

このアプローチでは，プログラム実行中に I/O を伴り，プログラムに何らかの入力を与えたときに，プうメソッドや乱数生成など，プログラムへの全ての

ログラムのどの部分が実行されたかを，開発者は俯

入力を記録したり，プログラムの内部状態のスナップ

瞰的に把握することができる．この例では，プレイショットを定期的に取得したりしておく．それらの情ヤの攻撃と敵キャラクタの当たり判定の処理として報を用いてプログラムの実行を再現するので，バグをActorLogic.cs ファイルなどのコードが実行されて再現させることができる．このアプローチでは，プロいることがわかる．ここで，開発者がソースコードをグラム実行の再現を繰り返すことによって，特定のバ

選択してクリックすると，そのソースコードで定義さグを何度も再現させることができ，過去に遡って原因れているクラスにおいて，定義されているメソッドのを追っていくことも可能である．しかし，ログを可視

一覧を図 2(2) に表示し，図 2(b) に選択されたソース化するアプローチと同様，入力に対するプログラム

コードを表示する．

の実行経路や変数の情報を即時確認し，その結果を(2) メソッドハイライト表示：

見ながら様々な入力を与え試行錯誤を繰り返し，効率

メソッド一覧を表示し，プログラムが実行したメソッド

よくバグ原因を探すことができない．また，完全な実を実時間にハイライト表示する．これにより，図 2(1)行の再現は技術的な障壁が高く，例えばマルチスレッと同様に，開発者はどのメソッドが実行されたかを俯

ドプログラムでは再現を保証できない [2] などの制約瞰的することが可能となる．この例では ActorLogic

がつき，適用範囲が限られるケースもある．

クラスの PlayerAttack メソッドが実行されている

本研究では，このような既存手法の問題点を解決
し，表 1(a) のような双方向性，実時間の高い様々なことがわかる．

(3) 実行経路表示：

プログラムのデバッグに際に，入力に対するプログラ

ソースコード上でプログラムが一定間隔で実行した

ムの実行経路や変数の情報を即時に可視化し，効率よ

ステートメントをハイライト表示する．これにより，いデバッグの支援を目指す．

入力に対してプログラムがどの箇所を実行したかを詳細に把握することができる．この例では，プレイヤ

3 提案するデバッグ手法

が近距離の敵キャラクタに対し (ダッシュボタンは押

本節では，提案するデバッグ手法について説明すさず) 攻撃ボタンのみを押して攻撃を行っており，実る．提案するデバッグ手法に基づいたデバッガを図

行経路情報を確認することで，敵キャラクタのいず2 に示す．デバッガはメインビュー (図 2(a)) とソー

れかに対し，enemy.HitPoint -= damagePoint がスコードビュー (図 2(b)) から成る，提案手法ではデ実行されていることが即座にわかる．ここで，例えバッグ対象のプログラムの実行経路情報と開発者がば DashBurron をユーザが押すと，図 3 に示すよう指定した変数の値を，一定の時間間隔 (例えば 0.1 秒に damagePoint *= 5 が実行されるようになることごと) でこれらのビューに反映し続ける．これにより，も即座に確認できる．

開発者は双方向性，実時間の高いプログラムのデバッ
グを効率よく行うことが可能となる．以降，1 節で紹(4) 選択したステートメント情報の表示：
図 2(3) でステートメントをクリックすると，ステー(1)
ࠈइ

(2)ࠈइ

(5)؇ईଅఈअԆਈ༈(6)؇ई̅ԉ‫܆‬คਈ༈(7)؇ईଅఈअԆ༏؇ई̅ԉ‫܆‬ค؆Ԇ

(8)؇ईଅఈ
अԆࠈइ

(a)
(3)ଅࠈഇഈࠈइ

(4)Ȇࠅࠈइ(b)
図2

提案するデバッグ手法

DashButtonȆȇĆ

ドファイル名を用いて，開発者は関心があるステート
メントに対して図 2(5),(6) で後述する監視対象の変数，監視区間とその条件の記述を行うことができる．

DashButtonȆȇĆ(5) 監視対象の変数を記述：
監 視 し た い 変 数 に つ い て ，表 2 に 従って 記 述

図3

ユーザ入力に対する実時間な実行経路の可視化

す る こ と で 指 定 す る こ と が で き る ．例 え ば「 enemy.HitPoint -= damagePoint」ステートメン

トメントに対してソースコード中で一意になるよう割トの実行前に enemy.HitPoint の値を監視したい場

り当てられた ID を表示する．この ID とソースコー

合には，図 2(5) の欄へ，監視対象のステートメントがあるファイルパス「GameForm.cs」を記述し (表 2 のの条件式ではスレッドの ID なども参照できるため，

項番 1)，次の行へ watch:8:bf:enemy.HitPoint とマルチスレッドプログラムにおいて，特定のスレッド記述する (表 2 の項番 2)．watch は変数監視を指示すの実行経路情報等を実時間に可視化することも可能

る「指令」であり，8 は図 2(4) で表示されるステート

である．

メントの ID である．bf はステートメントが実行する(7) 監視対象の変数，監視区間条件を更新するボタ

前で値を取得すること意味し (ステートメントを実行

ン：した後にしたい場合は af とする)， enemy.HitPoint

図 2(5),(6) への記述は更新ボタンを押すと反映される．

は，このステートメントを実行するときに評価する式となる．式の値は図 2(8) の表示欄へ，一定間隔ごと(8) 監視対象の変数の値を表示：
図 2(5) で設定した監視対象の変数の値を表示する．

の最新の値を表示する．

以上のように，提案するデバッグ手法を用いること(6) 監視区間を記述：

で，開発者はプログラムを停止させることなく，確認

開発者が確認したい監視区間とその条件を指定するこ

したい箇所を適宜絞り絞り込み，プログラムへ様々なとができる．例えば，foreach(var enemy ...) の入力を与えたときに，プログラムの実行経路情報や，

ループ の内側のステートメントは，複数の Enemy イ

変数の値がどのように変化するかを確認しながらデンスタンスそれぞれについて実行される．そのため，バッグを効率よく行うことが可能となる．

Enemy インスタンスがひとつでもステートメントを実行すると，そのステートメントがハイライトされ4 C#におけるデバッグ手法の実装

るため，インスタンスごとの区別がつかない．そこ

3 節で提案したでデバッグ手法を C# 上に実装しで，提案するデバッグ手法では，プログラムの実行

た．C# における本実装方式は，デバッグ対象のコー経路における特定の区間について，開発者が指定しドをデバッグ用のコードへ変換するステップ (図 4) とた条件を満たす場合にその区間の実行経路情報をハ変換したデバッグ用コードを実行し，プログラムを動イライトし，監視対象の変数の値を取得する機能を作させながらデバッグ情報を取得して可視化するス

用意している．例えば，プレイヤの近くにいる敵キャ

テップ (図 5) から成る．本節ではそれぞれのステッラクタのうち，タイプがスライムである敵キャラクプについて説明する．

タに関してのみ foreach(var enemy ...) の内側の情報を取得したい場合を考える．そのためには，図4. 1 デバッグ用コードへの変換

2(6) の欄へ，特定の条件についてのみ監視を行いた

図 4 に示すように，本実装方式ではデバッグ対象のい区間の開始ステートメント (例えば，図 2 の 15 行

C# のコードを変換し (図 4(1))，ステートメントご目) へ condstart:5:bf:enemy.TypeName=="Slime"とにデバッグ情報を取得するためのコードを埋め込(表 2 の項番 3)，区間の終了ステートメント (例えば，む．そして，ユーザは実時間にデバッグ用の情報を取

図 2 の 23 行目) へ condend:10:af (表 2 の項番 4) と得するライブラリ (図 4(2)) を対象コードにリンクしいう記述を行う．condstart，condend はそれぞれ区て実行するだけで，3 節に示したようなデバッガを起間の開始，終了を表す指令であり，condstart にお動することができる．そのため，デバッガを使用する

ける enemy.TypeName=="Slime" は，この条件が真にあたり，ユーザはデバッグ対象のソースコードに対となるときのみに，区間内のステートメントをハイラ

して一切変更を加える必要はないため，負担なく用いイトし，監視対象の変数の値を取得することを表す．

ることができる．以下，ソースコード 1 を変換した

これにより，開発者はループ処理 (for 文，foreach 文)

後のコード (ソースコード 2) を例にとり，本実装方等によりプログラム中で複数回実行される経路につ式の要点を簡潔に述べる．ソースコード 2 における

いて，条件を指定することで，ユーザが着目したい経_Logger クラスは図 4(2) のデバッグ用ライブラリに路や変数の値の情報のみを実時間に可視化できる．こより提供する．

表2
項番
1
2
3
4

指令種別
対象ソースコード
監視対象変数監視区間開始監視区間終了ソースコード 2
1

指令の一覧

記法
file:ファイルパス
watch:ステートメント ID:ステートメント前後の指定:値取得の式
condstart:ステートメント ID:ステートメント前後の指定:条件式
condend:ステートメント ID:ステートメント前後の指定

デバッグ用に変換されたコード

public void PlayerAttack(PlayerInput input, Player player, List<Enemy> enemyList)

2 {if (_Logger.IsLogging(0))_debug_PlayerAttack(input, player, enemyList);else_original_PlayerAttack(input, player, enemyList);

3
4
5
6
7 }
8
9

private void _debug_PlayerAttack(PlayerInput input, Player player, List<Enemy> enemyList)

10 {

13

var _l = _Logger.GetLogger(0, 0);
int damagePoint = _l.LogFunc(() => player.OffensivePower, 0);
if (_l.LogFunc(() => input.AttackButton, 1))

14

{

11
12

17

if (_l.LogFunc(() => input.DashButton, 2))_l.LogFunc(() => damagePoint *= 5, 3); //プレイヤがダッシュしていたらダメージ 5倍
foreach (var enemy in _l.LogFunc(() => enemyList, 4))

18

{

19

//……中略……

15
16_l.LogAction(() => enemy.EndPlayerCollision(), 10);

20
21

}

22

}

23 }

ドを，プログラムを停止させずに動的に切り替える．
(1)
अ̆



.NET


_original_PlayerAttack メソッドの定義はソース
コード 1 における PlayerAttack メソッドの定義と
‫܅‬༈

同じである．
(2)ଅࠈ̅
ࠈइԇ

図4

ଅࠈԇଅࠈ༅༅

本実装方式におけるデバッグ用コードへの変換

ソースコード 2 の 9 行目の_debug_PlayerAttack
メソッドは，オリジナルの PlayerAttack メソッドのコードへデバッグ用コードを埋め込んで変換した
コードである．_Logger.GetLogger メソッド (ソー本実装方式では，大規模なプログラムをデバッグ

スコード 2 の 11 行目) は実行中のスレッドに対応する際に，関心がない部位のデバッグ情報を取得しするロギング用_Logger クラスのインスタンスをて余計なオーバヘッドが発生することを避けるため，返すメソッドである．取得したロギング用インス

デバッグ情報を取得するか否かをメソッド単位で動的タンスは_l へ格納される．そして，_l.LogAction，に切り替える機構をもつ．ソースコード 2 の 1 行目

_l.LogFunc という，クロージャにしたステートメの PlayerAttack メソッドは，デバッグ対象のコーントとステートメント ID を引数とするロギング

ド中のメソッドに一意に割り振られた ID を引数とし用のメソッドで，全てのステートメントを置き換てとる_Logger.IsLogging メソッドの戻り値を確認える．例えば，player.OffensivePower という式はすることで，オリジナルのコードとデバッグ用のコー_l.LogFunc(() => player.OffensivePower, 0) へレッドと，デバッガ制御用スレッドが同時にアクセスଅఈ
ଅఈ
༇


…
1


N

ଅࠈഇഈ༏
अԆ

ȅ༅ԇ


ȅԇ؇ईଅఈअԆ༏؇ई̅ԉ‫܆‬คଅࠈഇഈ༏अԆ؇ईଅఈअԆ༏؇ई̅ԉ‫܆‬ค

を行うため，排他制御を行う必要がある．特に実行経路情報についてはこれらのスレッドが頻繁にアクセスを行うため，排他制御を確実に行いつつ，オーバヘッ
ドを軽減する必要がある．そこで，各ステートメント
における実行回数を記録するための変数に対し，C#の軽量排他制御オペレーションである Interlocked

図5

本実装方式におけるデバッグ実行の仕組みを用いてカウンタのインクリメントやリセットを行
い，スレッド間の排他制御を行うよう工夫した．

と置き換える (ソースコード 2 の 13 行目)．LogFunc

また，ソースコード 2 の_Logger.GetLogger は，は戻り値をもつステートメントの置換に使用し，デバッグ対象プログラムのメソッド呼び出しのたび

LogAction は戻り値をもたないステートメントの置に呼び出され，実行中のスレッドに対応する_Logger

換に使用する．例えば，LogFunc メソッドではソース

クラスのインスタンスが格納されているテーブルへ

コード 3 に示す擬似コードのように，ステートメン

アクセスし，対応するインスタンスを取得する．まトの実行前後で，実行経路情報の取得や監視対象のた，新たに生成されたスレッドが最初にアクセスした

ローカル変数の取得，監視区間条件の切り替えなどを場合は，対応するインスタンスを新規生成する必要も行う．Func<T> func にはクロージャとなったステーある．そのため，このテーブルへのアクセスも排他制

トメントが格納されている．これにより，ステートメ

御が必要となる．ここでも排他制御のオーバヘッドをントの実行 (ソースコード 3 の 15 行目) の前後で，実軽減するため，スレッドの生成に伴うテーブルへの書

行経路情報の取得 (ソースコード 3 の 4 行目) や監視

き込みは頻繁に行われないと想定し，読み込みについ対象のローカル変数の取得 (ソースコード 3 の 10-12ては複数のスレッドで同時に行うことができる排他制行目)，監視区間の開始点や終了点におけるロギング

御機構である ReadWriterLock を用いる工夫をした．

有効・無効の切り替え (ソースコード 3 の 5-8 行目)
をスレッドごとに行う．

5 ケーススタディ
本節では， CalculatorCSharp†4 (約 2.5KL) という

4. 2 デバッグ実行の仕組み

関数電卓のアプリケーションを題材として，提案手法

変換したデバッグ用コードは，図 5 のように実行すによるデバッグの例を示す．対象となるバグは，ケーる．本実装方式では，デバッガ制御用のスレッドはデススタディのために人為的に埋め込んだものである．

バッグ対象のプログラムのスレッドの 1 つとして動作

CalculatorCSharp の実行画面を図 6 に示す．今回埋する．デバッグ制御用以外の各スレッドは 1 つのス

め込んだバグは，「電卓の 1 や 2 など他のボタンを押テートメントを実行するたびに，デバッグ用コードにした場合は正しくその数字が表示されるが，3 ボタン

より記録する．このようにして集めた実行経路の情報を押したときにのみ画面上部の数字が更新されず，表と変数値の情報を取得して可視化する (図 5 の実践矢示が初期状態である 0 のままとなっている」というも印)．またデバッガ制御用スレッドは，監視対象変数のであり，期待する動作はボタン 3 を押したときに 3や監視区間と条件の情報をデバッガ利用者から取得と表示されることである．

し，その情報を各スレッドにおけるデバッグ用コード

提案手法を用いると図 7 に示すように，プログラ

が参照することにより，実行経路情報や変数値の記録

ムを停止させることなく，効率よくデバッグを行ってを行うかどうかを制御する (図 5 の点線矢印)．

いくことができる．以下に手順を述べる．

実行経路，変数値，監視対象変数や監視区間と条件の情報については，デバッグ対象のプログラムの各ス†4 https://github.com/Cleod9/CalculatorCSharpソースコード 3

ロギング用メソッドの擬似コード

1

public T LogFunc<T>(Func<T> func, int statementId)

2

{

3

//対象ステートメント実行前のデバッグ用処理

4

7

if(現スレッドでロギングは有効か？) 実行経路の記録
if(対象ステートメント実行前の監視区間開始 (condstart)指令があるか？){
if(condstart の条件式が真か？) 現スレッドでロギングを有効化
else 現スレッドでロギングを無効化

8

}

9
11

if(対象ステートメント実行前の監視区間終了 (condend)指令があるか？) 現スレッドでロギングを有効化
if(現スレッドでロギングは有効か？){
if(対象ステートメント実行前のwatch 指令があるか？) 変数値の記録

12

}

5
6

10

13
14

//対象ステートメントの実行

15

T result = func();

16
17//対象ステートメント実行後のデバッグ用処理

18//……中略……

19

return result;

20
21

}

switch 文が含まれているため，number_btn についてステートメント単位で実行経路を確認してみる．

3. 図 7(3) で，ボタン 3 を押したときの number_btnの実行経路を確認すると，numValue=3 が実行されていないことがわかる．また，このときボタン

1 などを押すと numValue=1 などはきちんと実行されていることもすぐにわかる．ボタン 3 を押図6

CalculatorCSharp の実行画面

したときの num.Name の値に原因がありそうなので，num.Name に変数監視式を設定する．

1. ボタン 3 を押すと，図 7(1) に示すように，Cal-4. 図 7(4) で は ，ボ タ ン 3 が 押 さ れ た と き の

cEngine.cs と Form1.cs がハイライトされ，これnum.Name の値を表示している．値は thre とらのソースコードが実行されており，どちらかのなっており，ボタン 3 の Name フィールド値を

ソースコードにバグの原因がありそうだというこ

three とすべきところ，変数名を誤って設定しとがわかる．今回，表示部分にバグがありそうなていたのがバグの原因であるとわかった．

ため，まず Form1.cs について詳しくみていく．

このように，提案手法を用いることで，様々な入力

2. 図 7(2) で は ，Form1.cs の メ ソッド 一 覧 を 表を与えながら，プログラムを停止させることなくプロ

示 し ，ま た ボ タ ン 3 を 押 す こ と で ，ど の メ

グラムの内部状態を即座に確認することで，効率よくソッドが実行されたかをハイライト表示してデバッグを行うことができる．

いる．ここでは，updateScreen，number_btn，

formatDisplay が実行されていることがわかる．

6 オーバヘッドの計測

それぞれのメソッドの定義を確認してみると，

本節では，本提案手法のオーバヘッドを計測した結

number_btn の定義にボタンの種別で分岐する果について述べる．以下のように GUI アプリ，ゲー

(1)ଅࠈ
ࠇਈ

(2)ଅࠈࠇਈ(4)अԆ؇ई༅ࠇਈ

(3)ଅࠈࠇਈ

図7

本実装方式におけるデバッグ実行の仕組み

ムアプリ，そして数値計算という 3 つプログラムを題
材にデバッグ時のオーバヘッドの計測を行った．

1. 前節で用いた CalculatorCSharp で数字の 0 のボタンを押す操作を行う．

2. PongCSharp†5 (約 0.38KL) というピンポンゲー
ムでゲームプレイする．一定間隔でユーザ入力受付，対戦相手の移動，ボールの軌道計算，描画な†5 https://github.com/Cleod9/PongCSharp

表3

1.CalculatorCSharp
2.PongCSharp
3.Fib(20)

実行回数
100,000
1,000
5,000

オーバヘッドの計測結果

オリジナルプログラム (単位 ms)
7,360(100%)
1,632(100%)
3,002(100%)どが行われる．

3. Fib：フィボナッチ数列を計算する．

デバッグ用プログラム (単位 ms)
10,344( 141%)
1,848( 113%)
112,533(3,749%)

れを解決するため，デバッガの UI 上で開発者が切り替えを行えるよう改良したい．第二に，デバッグ対象

3 つ目は本研究の対象領域ではないが，参考値としてのプログラムによっては，通常の実行速度だと実行経計測した．また，今回，実行経路情報の取得及び可視

路情報や変数情報の更新が早すぎて視認性が悪くな化のみを行い変数監視は行っていない．

る可能性もある．これを解決するため，実時間性，双評価結果を表 3 に示す．Fib のオーバヘッドが他の方向性を損なわない程度に実行速度を遅くする，ス

2 つよりも非常に大きくなっているのは，オリジナル

ロー再生機能をデバッガへ追加したい．第三に，原因のプログラムにおいて，1 つのステートメントで行わとなるファイルやメソッドの数が多い場合，よりデれている処理の計算量が小さいため，ステートメン

バッグを効率化するためには，原因を絞り込む手段もトごとにデバッグ情報を取得する処理のオーバヘッ

必要となる．例えば，デバッグ支援技術 [16] のひと

ドが相対的に大きくなるためである．それに対してつである Spectrum-based Fault Localization [7] のCalculatorCSharp，PongCSharp は 1 つのステート

考え方を応用するアプローチが考えられる．正常にメントで行われている処理の計算量が多いため，相対動作したときの実行経路情報と期待通りではない動的なオーバヘッドは Fib よりも小さくなった．特に，作をしたときの実行経路情報と比較し，その差異のPongCSharp については，.NET のライブラリ側で行部分のみを表示することで，バグの原因の箇所を絞われるゲーム画面の描画処理の計算量が大きいため，り込める可能性がある．ケーススタディのデバッグの

オーバヘッドがより小さくなっている．

例であれば，ボタン 1 を押したときの実行経路情報

このように，実用的なプログラムではオーバヘッドを記録しておき，ボタン 3 を押したときに，ボタンが 50%未満におさまっており，デバッグという目的

1 では通ってない実行経路情報のみを実時間にハイラを考慮すると十分許容範囲であると判断できる．

イトすれば，実行経路に差異のある number_btn メ
ソッドを早期に絞り込める可能性がある．第四に，複7 まとめと今後の課題

数回実行されたステートメントや，複数回評価され本稿では，プログラムを停止させることなくプログ

た監視対象の変数の式について，現状では詳細に確

ラムの実行経路情報と変数情報を，実時間に出力し表認するには監視区間を用いる必要があるが，監視区示するデバッグの手法を提案し，C# プログラムのデ間を用いる前にもう少し俯瞰的に確認できるとよい．

バッガとして実装した．本デバッグ手法を用いること

例えば，着目したい統計情報を色分けして表現する考により，開発者は実時間性や双方向性の高いプログラ

え方 [8, 9] を活用し複数回実行された実行経路の色を

ムを，停止させることなく，効率よくデバッグできる

変えて表示する，複数回評価された監視対象の式の結ようになる．

果については，値を全て (もしくはサンプリングして今後の課題は 4 つある．第一に，本実装方式ではデバッグ実行のオーバヘッドをなるべく軽減したい．

いくつか) 保持しておき，どの値が何回現れたかを表示する，などの工夫が考えられる．

そのため，メソッドごとにデバッグ用実行するか，オ

また，これらの機能を実装した上で，実用的に用いリジナルのプログラムを実行するかを切り替えられられているような様々なプログラムを題材として，提る機構を実装している．しかし，デバッガから開発者

案手法の有効性を確認していきたい．

が切り替える機能については現状備わっていない．こ参 考 文 献[ 1 ] Banken, H., Meijer, E., and Gousios, G.: Debugging Data Flows in Reactive Programs, Proceedingsof the 40th International Conference on Software
Engineering, ICSE ’18, New York, NY, USA, ACM,
2018, pp. 752–763.
[ 2 ] Barr, E. T. and Marron, M.: Tardis: Aﬀordable
Time-travel Debugging in Managed Runtimes, Proceedings of the 2014 ACM International Conference
on Object Oriented Programming Systems Languages & Applications, OOPSLA ’14, New York,NY, USA, ACM, 2014, pp. 67–82.
[ 3 ] Barr, E. T., Marron, M., Maurer, E., Moseley, D., and Seth, G.: Time-travel Debugging
for JavaScript/Node.Js, Proceedings of the 2016
24th ACM SIGSOFT International Symposium on
Foundations of Software Engineering, FSE 2016,
New York, NY, USA, ACM, 2016, pp. 1003–1007.
[ 4 ] Bell, J., Sarda, N., and Kaiser, G.: Chronicler: Lightweight recording to reproduce field failures, 2013 35th International Conference on Software Engineering (ICSE), May 2013, pp. 362–371.
[ 5 ] Beller, M., Spruit, N., Spinellis, D., and Zaidman, A.: On the Dichotomy of Debugging Behavior Among Programmers, Proceedings of the40th International Conference on Software Engineering, ICSE ’18, New York, NY, USA, ACM,
2018, pp. 572–583.
[ 6 ] Czyz, J. K. and Jayaraman, B.: Declarative
and Visual Debugging in Eclipse, Proceedings of the2007 OOPSLA Workshop on Eclipse Technology
eXchange, eclipse ’07, New York, NY, USA, ACM,
2007, pp. 31–35.
[ 7 ] de Souza, H. A., Chaim, M. L., and Kon, F.:
Spectrum-based Software Fault Localization: A
Survey of Techniques, Advances, and Challenges,
CoRR, Vol. abs/1607.04347(2016).
[ 8 ] Eick, S. C., Steﬀen, J. L., and Sumner, E. E.:
Seesoft-a tool for visualizing line oriented software
statistics, IEEE Transactions on Software Engineering, Vol. 18, No. 11(1992), pp. 957–968.
[ 9 ] Gill, A. and Runciman, C.: Haskell Program Coverage, Proceedings of the ACM SIGPLAN
Workshop on Haskell Workshop, Haskell ’07, New
York, NY, USA, ACM, 2007, pp. 1–12.
[10] Koju, T., Takada, S., and Doi, N.: An Eﬃcientand Generic Reversible Debugger Using the Virtual
Machine Based Approach, Proceedings of the 1st

ACM/USENIX International Conference on Virtual Execution Environments, VEE ’05, New York,NY, USA, ACM, 2005, pp. 79–88.
[11] Li, X. and Flatt, M.: Medic: Metaprogramming and Trace-oriented Debugging, Proceedings ofthe Workshop on Future Programming, FPW 2015,
New York, NY, USA, ACM, 2015, pp. 7–14.
[12] Lin, Y., Sun, J., Xue, Y., Liu, Y., and Dong, J.:
Feedback-Based Debugging, 2017 IEEE/ACM 39th
International Conference on Software Engineering(ICSE), May 2017, pp. 393–403.
[13] Ressia, J., Bergel, A., and Nierstrasz, O.:
Object-centric Debugging, Proceedings of the 34th
International Conference on Software Engineering,
ICSE ’12, Piscataway, NJ, USA, IEEE Press, 2012,
pp. 485–495.
[14] Rosenblum, D. S.: A Practical Approach toProgramming With Assertions, IEEE Trans. Softw.
Eng., Vol. 21, No. 1(1995), pp. 19–31.
[15] Terada, M.: ETV: A Program Trace Player forStudents, Proceedings of the 10th Annual SIGCSE
Conference on Innovation and Technology in Computer Science Education, ITiCSE ’05, New York,NY, USA, ACM, 2005, pp. 118–122.
[16] Wong, W. E., Gao, R., Li, Y., Abreu, R., and
Wotawa, F.: A Survey on Software Fault Localization, IEEE Transactions on Software Engineering,Vol. 42, No. 8(2016), pp. 707–740.
[17] Yanagisawa, Y., Kourai, K., and Chiba, S.: A
Dynamic Aspect-oriented System for OS Kernels,
Proceedings of the 5th International Conference
on Generative Programming and Component Engineering, GPCE ’06, New York, NY, USA, ACM,
2006, pp. 69–78.
[18] Yin, H., Bockisch, C., and Aksit, M.: A Pointcut Language for Setting Advanced Breakpoints,
Proceedings of the 12th Annual International Conference on Aspect-oriented Software Development,
AOSD ’13, New York, NY, USA, ACM, 2013,
pp. 145–156.
[19] Zhang, C., Yan, D., Zhao, J., Chen, Y., andYang, S.: BPGen: An Automated Breakpoint
Generator for Debugging, Proceedings of the 32Nd
ACM/IEEE International Conference on Software
Engineering - Volume 2, ICSE ’10, New York, NY,
USA, ACM, 2010, pp. 271–274.
[20] 丹野治門: ゲームプログラムに適したリアルタイム性の高いデバッガの提案と実装, 情報処理学会論文誌プロ
グラミング（PRO）, Vol. 1, No. 2(2008), pp. 42–56.