view paper/test.tex @ 14:19be75493fbb

fix.
author koba <koba@cr.ie.u-ryukyu.ac.jp>
date Tue, 15 Feb 2011 18:04:22 +0900
parents 028ed9741872
children
line wrap: on
line source

\chapter{テスト環境の構築} \label{chapter:test}
本研究では、逐次型プログラムとして Super Dandy の OpenGL バージョン、Cerium 
の Rendering Engine のみを使用したバージョンを用意した。これらと Task Dandy 
を用いてその挙動をテストし、全てのバージョンにおいて同じ挙動をしている事を
確認できるテスト環境を構築する。ここではテスト環境のために追加した機能に
ついて説明する。

\section{Capture モードと Trace モードの実装}
ゲームにおいてプレイヤーからの入力は制御不能なランダム要素であり、
バグを再現することを困難にする(\ref{sec:player}節)。
そこでプレイヤーからの入力を1フレーム毎に記録し、バイナリデータとして
書き出す Capture モードと書き出されたバイナリデータを読み込み、プレイヤーの
入力を再現する Trace モードを実装した。どちらのモードも実行ファイルに
オプションとファイル名を付けることによって起動することが出来る。

\begin{verbatim}
% ./demo -capture capture.dat
Start Capture mode.

% ./demo -trace capture.dat
Start Trace mode.
\end{verbatim}

2 つのモードでは TraceBuff という単方向リスト型のバッファを使用している。
Capture モードではバッファサイズが足りなくなると新たな TraceBuff を allocate 
して next ポインタにアドレスを格納する。バイナリデータに書き出す時はリストの
先頭から順番に書き出していく。

Trace モードではバイナリデータのサイズを計算し、予め必要なサイズの TraceBuff
を allocate しておく。
(図\ref{fig:pad_buff})

\begin{figure}[h]
\begin{center}
\includegraphics[scale=0.6]{images/pad_buff.pdf}
\end{center}
\caption{TraceBuff}
\label{fig:pad_buff}
\end{figure}

この 2 つのモードは用意された 3 つの Super Dandy 全てに組み込まれている。
よって旧バージョンで入力データを記録し、記録したデータを新バージョンで
読み込むことも可能である。これにより、両バージョンで異なる動きが見られた
場合、そこにバグが潜んでいると仮定することができる。

\section{SPE における乱数生成の欠点}\label{sec:create_random}
乱数のランダム性はバグの再現性を落とすが、乱数生成器を無効にするか、定数で
シードすることによって再現性を下げることなく、テストすることが出来る
(\ref{sec:random}節)。

通常のシーケンシャルなプログラムでは 1 つの乱数列から順番に各 Move 関数が
乱数を取得し、使用する。しかし並列プログラムでは Move 関数が Task として 
SPE に送られる。各 SPE 内では以下のような処理が行われる。

\begin{enumerate}
\item 定数でシードし、乱数列を生成する。
\item Task1 で乱数列の先頭から 1 番目の乱数を取得する
\item Task2 で乱数列の先頭から 2 番目の乱数を取得する
\item Task3 で乱数列の先頭から 3 番目の乱数を取得する
\end{enumerate}

乱数列は SPE 毎に独自に生成されるため、各 Task(=Move) が受け取る乱数は
逐次実行した時とは異なる値となってしまう。また、SPE 内でも Task 同士に
依存関係を持たせない限り、Task の実行順序が保証されないのでこれも受け取る
乱数が不定となる原因となる。(図\ref{fig:spe_random})

\begin{figure}[h]
\begin{center}
\includegraphics[scale=0.4]{images/spe_random.pdf}
\end{center}
\caption{SPE 内での乱数の生成}
\label{fig:spe_random}
\end{figure}
OB
\newpage

そこで予め PPE 内で乱数列を生成し、inData として Task に渡しておく。
Task Dandy では Task の生成、定義がされるタイミングは Super Dandy における
Move 関数や Collision 関数が実行されるタイミングと同じである為、渡される乱数
は Super Dandy と同じ乱数となる。(図\ref{fig:ppe_random})

\begin{figure}[h]
\begin{center}
\includegraphics[scale=0.5]{images/ppe_random.pdf}
\end{center}
\caption{PPE 内での乱数の生成と乱数の受け渡し}
\label{fig:ppe_random}
\end{figure}

\section{テストログに記述する情報とそのタイミング}
シーケンシャルなプログラムを Task に分割して並列実行する際に、新たに発生する
バグとして、本研究では以下の項目に焦点を当てた。

\begin{itemize}
\item Task 間のデータの同期
\item Task の実行順序
\item Task の定義
\end{itemize}

このうち、Task の定義については、定義される内容が非常に小さい為、Task の
inData や outData を調べるといった従来のテスト手法でも十分にテストが可能で
ある。その他の 2 つについては、いずれも衝突判定の際に生じるバグである。
(図\ref{fig:test_log})

\newpage

\begin{figure}[h]
\begin{center}
\includegraphics[scale=0.8]{images/test_log.pdf}
\end{center}
\caption{Task Dandy で起こりうるバグ}
\label{fig:test_log}
\end{figure}

そこで、オブジェクトが被弾した時、そしてオブジェクトが生成された時にテスト
ログを取ることで効率的にバグを発見することができると考えた。以下に実際に
収集したテストログの例を示す。

\begin{verbatim}
F64: CREATE  [NAME]enemy_greenclab_0  [COORD]x= 120.000000  y= -128.000000
                                             vx= 0.000000  vy= 4.000000
F85: DELETE  [NAME]enemy_greenclab_0  [COORD]x= 120.000000  y= -44.000000
                                             vx= 0.000000  vy= 4.000000
             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
\end{verbatim}

それぞれのパラメータの詳細は次のとおりである。

\newpage

\begin{itemize}
\item F64,F85\\
  生成、もしくは被弾した時の経過フレーム。
\item CREATE,DELETE\\
  CREATE ならオブジェクトが生成された、DELETE ならオブジェクトが被弾した事を
  表す。
\item NAME\\
  オブジェクトの種類と ID。ID はオブジェクトの種類毎に 0 から順番に付けられ
  るのでどのオブジェクトの情報なのかを特定できる。
\item COORD\\
  オブジェクトのxy座標とxy方向の速度。
\item BULLET
  その瞬間に画面内に存在した弾の数。差異があれば同期が取れていないことを
  示す。
\end{itemize}

これにより、フレーム単位でどのオブジェクトが生成、または被弾したのか知ること
ができる。trace モードでプレイヤーの入力を固定し、各バージョンでテストログを
取り、その差異を調べることでバグが発生している時間や場所を特定することが
できる。

\section{描画処理を行わないビデオモード}\label{sec:video_none}
Cerium では Rendering Engine において 3 つの Task を用いて画面の描画処理を
行っている(\ref{sec:rendering}節)。Cerium では複数の環境で動作するように
オブジェクトを画面のフレームバッファに直接書き出すモードや、SDL のバッファに
書き出すモードなど、複数のビデオモードが存在する。
(図\ref{fig:video_mode})

しかし、プレイヤーの入力をバイナリデータから読み出す場合、処理の詳細が
知りたい場合を除いて画面の描画処理は不要となる。
そこでテスト用に画面の描画処理を行わないモードを Cerium に実装した。
これは、Cerium 内で画面のバッファを確保する処理をしている箇所と、描画用の 
Task を生成する処理をしている箇所をスルーすることで描画処理と無駄なメモリ
確保を行わずにテストを行うことができるビデオモードである。
(図\ref{fig:video_none})

\begin{figure}[h]
\begin{center}
\includegraphics[scale=0.6]{images/video.pdf}
\end{center}
\caption{通常のビデオモード}
\label{fig:video_mode}
\end{figure}

\begin{figure}[h]
\begin{center}
\includegraphics[scale=0.8]{images/video_none.pdf}
\end{center}
\caption{描画処理を行わないビデオモード}
\label{fig:video_none}
\end{figure}