6.9. デバッグ支援 - トランスポート分析機能

概要

PiaTransport には、通信状況を分析するトランスポート分析機能が用意されています。以下、トランスポート分析機能の内容、コーディング例、グラフ化ツールの使用方法について解説します。

取得可能なデータ

トランスポート分析機能では、以下の種類のデータを定期的に取得することができます。

  • 送受信されたパケット数とそのデータサイズ
  • 送受信されたプロトコル用メッセージの種類と、そのデータサイズや個数
  • ステーション毎の RTT と、その分析期間での RTT 測定値の最小値と最大値
  • ステーション毎のパケロス率
  • ディスパッチ回数

送信データについては、ユニキャスト送信とブロードキャスト送信それぞれの分析データを取得することもできます。

ステーション毎の RTT については、その分析期間以前に測定した結果が一部影響する場合があります。RTT 測定値の最小値と最大値については、その分析期間における値となっています。そのため、RTT の値が RTT の最小値と最大値の間に収まらない場合があります。

後述する PiaTransportAnalyzer ツールを用いると、上記のデータを時間軸に沿ってプロットしたグラフを得ることができ、通信状況を視覚的に把握しやすくなります。

分析データの自動プリント機能

トランスポート分析機能はTransportAnalyzer クラスによって提供されます。このクラスのインスタンスは PiaTransport によって暗黙的にシングルトンとして構築/破棄されます。

TransportAnalyzer クラスには、分析データをコンソールへ自動的にプリントする機能があります。この機能を利用するには、Transport::Setting 構造体の analysisInterval メンバに正の値をセットして Transport クラスのインスタンスを作成します。

コード 6-34. TransportAnalyzer の計測データ自動プリント機能
nn::pia::transport::Transport::Setting setting;
 
// (他のメンバへの代入は省略)
 
setting.analysisInterval = 5; // 単位は秒
nn::pia::transport::Transport::CreateInstance(setting);

上記のような設定を行っておくと、5秒おきに以下のようなデータがコンソールにプリントされます。

コード 6-35. プリントされる分析データの例
[Pia:20204] [Analysis] ------ BEGIN(Pia Send, 45.551 sec. passed) ------
[Pia:20204] [Analysis] ProtocolId, TotalNum, TotalSize, AverageSize, Protocol(port)
[Pia:20204] [Analysis] 0xffffffff, 252, 152260, 604, Packet
[Pia:20204] [Analysis] 0x03000001, 0, 0, -1, Local(1)
[Pia:20204] [Analysis] 0x01000000, 0, 0, -1, Station(0)
[Pia:20204] [Analysis] 0x02000000, 0, 0, -1, Mesh(0)
[Pia:20204] [Analysis] 0x06000000, 68, 2448, 36, Rtt(0)
[Pia:20204] [Analysis] 0x02100000, 5, 180, 36, SyncClock(0)
[Pia:20204] [Analysis] 0x02000001, 2, 88, 44, Mesh(1)
[Pia:20204] [Analysis] 0x20000000, 155, 136700, 881, Unreliable(0)
[Pia:20205] [Analysis] 0x30000000, 25, 2764, 110, Reliable(0)
[Pia:20205] [Analysis] ---------------------------- END ----------------------------
[Pia:20205] [Analysis] ------ BEGIN(Pia Send Unicast, 45.551 sec. passed) ------
[Pia:20205] [Analysis] ProtocolId, TotalNum, TotalSize, AverageSize, Protocol(port)
[Pia:20205] [Analysis] 0xffffffff, 175, 62408, 356, Packet
[Pia:20205] [Analysis] 0x03000001, 0, 0, -1, Local(1)
[Pia:20205] [Analysis] 0x01000000, 0, 0, -1, Station(0)
[Pia:20205] [Analysis] 0x02000000, 0, 0, -1, Mesh(0)
[Pia:20205] [Analysis] 0x02000001, 2, 88, 44, Mesh(1)
[Pia:20205] [Analysis] 0x06000000, 51, 1836, 36, Rtt(0)
[Pia:20205] [Analysis] 0x20000000, 95, 50540, 532, Unreliable(0)
[Pia:20205] [Analysis] 0x30000000, 25, 2764, 110, Reliable(0)
[Pia:20205] [Analysis] 0x02100000, 5, 180, 36, SyncClock(0)
[Pia:20205] [Analysis] ---------------------------- END ----------------------------
[Pia:20205] [Analysis] ------ BEGIN(Pia Send Broadcast, 45.551 sec. passed) ------
[Pia:20205] [Analysis] ProtocolId, TotalNum, TotalSize, AverageSize, Protocol(port)
[Pia:20205] [Analysis] 0xffffffff, 77, 89852, 1166, Packet
[Pia:20205] [Analysis] 0x06000000, 17, 612, 36, Rtt(0)
[Pia:20205] [Analysis] 0x02100000, 0, 0, -1, SyncClock(0)
[Pia:20205] [Analysis] 0x20000000, 60, 86160, 1436, Unreliable(0)
[Pia:20205] [Analysis] ---------------------------- END ----------------------------
[Pia:20205] [Analysis] ------ BEGIN(Pia Receive, 45.551 sec. passed) ------
[Pia:20205] [Analysis] ProtocolId, TotalNum, TotalSize, AverageSize, Protocol(port)
[Pia:20205] [Analysis] 0xffffffff, 377, 312340, 828, Packet
[Pia:20205] [Analysis] 0x20000000, 254, 289712, 1140, Unreliable(0)
[Pia:20205] [Analysis] 0x03000001, 0, 0, -1, Local(1)
[Pia:20205] [Analysis] 0x01000000, 0, 0, -1, Station(0)
[Pia:20205] [Analysis] 0x06000000, 97, 3492, 36, Rtt(0)
[Pia:20205] [Analysis] 0x02000000, 0, 0, -1, Mesh(0)
[Pia:20205] [Analysis] 0x02000001, 2, 1328, 664, Mesh(1)
[Pia:20205] [Analysis] 0x30000000, 23, 2548, 110, Reliable(0)
[Pia:20205] [Analysis] 0x02100000, 5, 180, 36, SyncClock(0)
[Pia:20205] [Analysis] ---------------------------- END ----------------------------
[Pia:20205] [Analysis] ------ BEGIN(Pia Connection info, 45.551 sec. passed) ------
[Pia:20205] [Analysis] StationIndex, RTT, RttMin, RttMax, PacketLoss
[Pia:20205] [Analysis] 0x00, 66, 33, 66, 2.17
[Pia:20205] [Analysis] 0x02, 49, 33, 66, 6.45
[Pia:20205] [Analysis] 0x03, 66, 66, 66, 5.36
[Pia:20205] [Analysis] ---------------------------- END ----------------------------
[Pia:20205] [Analysis] ------ DispatchCount:600 ------

プリントされる分析データは複数行に及びますので、出力の完了までに比較的時間がかかります。アプリケーションの挙動を損なわないために、時間間隔は最低でも数秒以上の値を設定する必要があります。

また、トレースを大量に出力する設定では、分析データとトレース出力が混ざることがあります。この場合、後述する分析データ解析ツールが正しく分析データを解釈できず、エラーとなることがあります。そのため、トランスポート分析データを取得する際は、可能な限りトレース出力を抑制することをおすすめします。

明示的なトランスポート分析データの取得

前述した自動プリント機能を使わずに、アプリケーションがトランスポート分析データを取得することもできます。自動プリント機能を使わないようにするには、Setting 構造体の analysisInterval メンバ変数に 0 をセットする必要があります。

コード 6-36. 自動プリント機能を使わない設定
nn::pia::transport::Transport::Setting setting;

// (他のメンバへの代入は省略)

setting.analysisInterval = 0;
nn::pia::transport::Transport::CreateInstance(setting);

上記の設定を行った場合、アプリケーションは明示的に TransportAnalyzer インスタンスにデータの更新と取得を指示する必要があります。

コード 6-37. 明示的なデータの取得とプリント
// トランスポート分析機能を用い、取得したデータを5秒間隔でコンソールに出力します。
// 以下のコードはアプリケーションのメインループで繰り返し呼び出されることを想定しています。
nn::pia::transport::TransportAnalyzer* pAnalyzer = nn::pia::transport::TransportAnalyzer::GetInstance();
if(pAnalyzer)
{
    static int32_t s_Counter = 0;
    const int32_t Period = 5 * FPS;
    if(s_Counter % Period == 0)
    {
        nn::pia::Result r = pAnalyzer->Update();
        PIA_SAMPLE_ASSERT(r.IsSuccess());
        nn::pia::transport::TransportAnalysisData data = pAnalyzer->GetTransportAnalysisData();
 
        // 取得したトランスポート分析データをコンソールにプリントします。
        // 通常は第 1 引数での詳細表示指定は false で充分ですが、Pia 内部で暗黙的に
        // 使用されているプロトコルなどの情報も表示するときに true を指定します。
        // ユニキャスト送信とブロードキャスト送信それぞれの分析データも出力したい場合は、第 2 引数に false を指定します。
        data.Print(false, true);
    }
    ++s_Counter;
}

Release ビルドでの分析データ取得

Release ビルドにおいては、分析データはコンソールにプリントされません。Release ビルドで分析データを取得するには以下の二つの方法があります。

  1. 前述の通り、アプリケーションが明示的に transport::TransportAnalysisData 構造体のインスタンスを取得し、そのメンバ変数を参照する
  2. アプリケーションが定義したコールバック関数を登録し、分析データ文字列を得る

以下、プリントコールバック関数を登録する方法について解説します。

コンソールへのプリントが可能なコールバック関数を用意し、transport::TransportAnalyzer::RegisterPrintCallback() でコールバックを登録します。登録によって分析データ文字列がコールバックに渡されるようになるので、コンソールへのプリントが可能になります。

コード 6-38. コールバック関数の登録
// アプリケーション定義のコールバック関数を用意しておきます。
// 分析データの文字列が pStr に渡されます。
void appDefinePrintCallback(const char* pStr)
{
    printFunction(pStr);
    printFunction("\n");
}
 

void registerCallback()
{
    // アプリケーション定義のコールバック関数を登録し、分析データのプリントを可能にします。
    nn::pia::transport::TransportAnalyzer* pAnalyzer = nn::pia::transport::TransportAnalyzer::GetInstance();
    PIA_SAMPLE_ASSERT(PIA_IS_VALID_POINTER(pAnalyzer));
    pAnalyzer->RegisterPrintCallback(appDefinePrintCallback);
}

分析したデータログのグラフ化 

分析データを定期的に出力したログをグラフ化するツールとして、以下のディレクトリに Excel ファイルを用意しています。

$CTRPIA_ROOT/tools/TransportAnalyzer/PiaTransportAnalyzer.xlsm

この PiaTransportAnalyzer.xlsm を開き、top シートにある「Start Analyzing」ボタンを押して適切なログファイルを選択すると、分析データのグラフが作成されます。

参考:

・PiaTransportAnalyzer はMicrosoft Excel 2010 で作成しています。

・この Excel ファイルにはマクロが組み込まれています。Microsoft Excel のセキュリティ機能としてマクロが無効にされている場合、グラフ化機能は使用できません。有効化してから使用する必要があります。

・ユニキャスト送信とブロードキャスト送信それぞれの分析データのグラフ化には対応していません。

グラフ説明

送受信流量系グラフ

対象期間中に送受信された全パケットおよび各種プロトコル用メッセージの流量を示すグラフです。グラフ右側の凡例は、全パケットデータでは Packet、それ以外は各種プロトコルの名前およびポート番号が表記されます。グラフタイトルの Send / Receive は送受信の区別を示し、縦軸の値は traffic num では全パケット(もしくは各種プロトコル用メッセージ)の個数、traffic volume ではサイズになります。
定期的にログ出力されていることを前提に、出力時刻の経過時間を利用して 1 秒毎の送受信流量を計算しグラフ化しています。

参考:

・traffic num の場合、全パケット数よりもプロトコル用メッセージ数が多くなる場合がありますが、これは Pia のバンドリング機能で 1 パケットに複数のメッセージがまとめられて送信されることがあるためです。

・ログをプリントする間隔が長すぎる場合などは値が平均化されてしまい、通信状況の特徴が隠されてしまうことがあります。

通信相手毎の RTT グラフ

対象期間中に通信相手毎に計測した RTT を示すグラフです。グラフ右側の凡例は、通信相手を示す StationIndex の 16 進表記になります。未接続の期間や、正しい RTT が取得できていない期間については、値 0 としてグラフ化されます。

通信相手毎のパケロス率グラフ

対象期間中に通信相手毎に計算したパケロス率を示すグラフです。グラフ右側の凡例は、通信相手を示す StationIndex の 16 進表記になります。

区切り線の挿入

アプリケーション側が任意のタイミングで TransportAnalyzer クラスの区切り線挿入用関数を呼び出すことで、グラフに区切り線を挿入することができます。

コード 6-39. 区切り線挿入用関数の呼出
// グラフ上に区切り線を挿入するためのログ出力を行います。
// 以下のコードはアプリケーションの任意のタイミング(例えばシーン切替時など)で呼び出すことを想定しています。
nn::pia::transport::TransportAnalyzer* pAnalyzer = nn::pia::transport::TransportAnalyzer::GetInstance();
if(pAnalyzer)
{
    // 区切り線名 SceneChange を挿入
    pAnalyzer->SetMarker("SceneChange");
}

区切り線の凡例は、グラフ右側のデータを示す凡例の下へ追記されます。
前項で示した各グラフ例では、区切り線名 "JoinEvent", "LeaveEvent" をそれぞれ数回挿入しています。

注意:

・区切り線名には空白類文字(空白やタブ、改行など)を使用してはいけません。

・区切り線挿入用関数を短期間に複数回呼び出した場合、(区切り線名が同一か否かを問わず)グラフ生成時に区切り線が重なって表示される場合があります。

グラフの設定変更

PiaTransportAnalyzer の top シートにある "Graph Settings" 以下の項目を変更することで、生成されるグラフの各種設定が変更されます。

Graph Settings
Height グラフの高さ設定。 S, M, L の 3 段階。
Width グラフの幅設定。 S, M, L, LL, XL の 5 段階。
Thickness グラフの線の太さ・マーカーの大きさ設定。 S, M, L の 3 段階
Scale グラフの縦軸を対数グラフとするか否かの設定。
Normal : 通常のグラフ  Logarithm : 対数グラフ
参考:

Scale : Logarithm (対数グラフ)の場合は値 0 がプロットできないため、折れ線グラフ形式ではなく散布図形式になります。

グラフの画像出力

生成されたグラフのシート上の「Export Images As PNG / GIF / JPEG」ボタンを押すことで、各グラフを任意の画像形式で出力します。
出力先は PiaTransportAnalyzer と同じディレクトリ内です。