C++Build Insights SDK

C++ Build Insights SDK は、Visual Studio 2017 以降と互換性があります。 これらのバージョンのドキュメントを表示するには、この記事の Visual Studio バージョン セレクター コントロールを Visual Studio 2017 以降に設定します。 このページの目次の一番上にあります。

C++ Build Insights SDK は、C++ Build Insights プラットフォーム上でパーソナライズされたツールを作成できる API のコレクションです。 このページでは、作業を開始するために役立つ概要を説明します。

SDK の取得

次の手順に従って、C++ Build Insights SDK を NuGet パッケージとしてダウンロードできます。

  1. Visual Studio 2017 以降で、新しい C++ プロジェクトを作成します。
  2. [ソリューション エクスプローラー] ウィンドウで、プロジェクトを右クリックします。
  3. コンテキスト メニューから [NuGet パッケージの管理] を選択します。
  4. 右上で、[nuget.org] パッケージ ソースを選択します。
  5. Microsoft.Cpp.BuildInsights パッケージの最新バージョンを検索します。
  6. [インストール] を選択します。
  7. ライセンスに同意します。

SDK の一般的な概念に関する情報を参照してください。 公式の C++ Build Insights サンプルの GitHub リポジトリにアクセスして、SDK を使用する C++ アプリケーションの実例を参照することもできます。

トレースの収集

C++ Build Insights SDK を使用して、MSVC ツールチェーンから発生するイベントを分析するには、最初にトレースを収集する必要があります。 この SDK では、基になるトレース テクノロジとして Event Tracing for Windows (ETW) が使用されます。 トレースを収集するには、次の 2 つの方法があります。

方法 1: Visual Studio 2019 以降の vcperf を使用する

  1. VS 2019 用の x64 Native Tools コマンド プロンプトを管理者特権で開きます。

  2. 次のコマンドを実行します。vcperf /start MySessionName

  3. プロジェクトをビルドする。

  4. 次のコマンドを実行します。vcperf /stopnoanalyze MySessionName outputTraceFile.etl

    重要

    vcperf を使用してトレースを停止する場合は、/stopnoanalyze コマンドを使用します。 通常の /stop コマンドによって停止されたトレースの分析に、C++ Build Insights SDK を使用することはできません。

方法 2: プログラムを使用する

プログラムでトレースを開始および停止するには、C++ Build Insights SDK の次のトレース収集関数のいずれかを使用します。 これらの関数呼び出しを実行するプログラムには、管理特権が必要です。 管理特権を必要とするのは、トレースの開始および停止関数のみです。 C++ Build Insights SDK 内のその他のすべての関数は、管理特権がなくても実行できます。

機能 C++ API C API
トレースの開始 StartTracingSession StartTracingSessionA
StartTracingSessionW
トレースの停止 StopTracingSession StopTracingSessionA
StopTracingSessionW
トレースを停止し、
即座に結果を分析する
StopAndAnalyzeTracingSession StopAndAnalyzeTracingSessionA
StopAndAnalyzeTracingSession
トレースを停止し、
即座に結果を再ロギングする
StopAndRelogTracingSession StopAndRelogTracingSessionA
StopAndRelogTracingSessionW

以降のセクションでは、分析または再ログ セッションを構成する方法について説明します。 これは、機能を結合した関数 (StopAndAnalyzeTracingSession など) に必要です。

トレースの使用

ETW トレースを収集したら、C++ Build Insights SDK を使用してアンパックします。 SDK によって、ツールをすばやく開発できる形式でイベントが提供されます。 SDK を使用せずに未処理の ETW トレースを使用することはお勧めしません。 MSVC で使用されるイベント形式はドキュメント化されておらず、非常に大規模なビルドにスケーリングできるように最適化されており、理解するのは困難です。 さらに、C++ Build Insights SDK API は安定していますが、未処理の ETW トレースは予告なしに変更される可能性があります。

機能 C++ API C API メモ
イベント コールバックの設定 IAnalyzer
IRelogger
ANALYSIS_CALLBACKS
RELOG_CALLBACKS
C++ Build Insights SDK によって、コールバック関数を介してイベントが提供されます。 C++ では、IAnalyzer または IRelogger を継承するアナライザーまたは再ロガー クラスを作成することによって、コールバック関数を実装します。 C では、コールバックをグローバル関数で実装し、それらを指すポインターを ANALYSIS_CALLBACKS または RELOG_CALLBACKS 構造体で提供します。
グループの構築 MakeStaticAnalyzerGroup
MakeStaticReloggerGroup
MakeDynamicAnalyzerGroup
MakeDynamicReloggerGroup
C++ API には、複数のアナライザーおよび再ロガー オブジェクトをグループ化するヘルパー関数と型が用意されています。 グループは、複雑な分析をより簡単な手順に分割するための便利な方法です。 vcperf は、この方法で整理されています。
分析または再ログ 分析
再ログ
AnalyzeA
AnalyzeW
RelogA
RelogW

分析と再ログ

トレースの使用は、分析セッションまたは再ログ セッションのいずれかを介して行われます。

ほとんどのシナリオでは、通常の分析の使用が適しています。 この方法を使用すると、出力形式 (printf テキスト、xml、JSON、データベース、REST 呼び出しなど) を柔軟に選択できます。

再ログは、ETW 出力ファイルを生成する必要がある特定用途の分析に使用されます。 再ログを使用すると、C++ Build Insights イベントを独自の ETW イベント形式に変換できます。 再ログの使用が適切なのは、C ++ Build Insights データを既存の ETW ツールとインフラストラクチャにフックする場合です。 たとえば、vcperf では、再ログ インターフェイスが使用されます。 これは、ETW ツールである Windows Performance Analyzer で理解できるデータを生成する必要があるためです。 再ログ インターフェイスの使用を計画する場合は、ETW の動作方法について事前の知識が多少必要です。

アナライザー グループの作成

グループの作成方法を理解することが重要です。 アクティビティ開始イベントを受け取るごとに Hello, world! を出力するアナライザー グループの作成方法の例を次に示します。

using namespace Microsoft::Cpp::BuildInsights;

class Hello : public IAnalyzer
{
public:
    AnalysisControl OnStartActivity(
        const EventStack& eventStack) override
    {
        std::cout << "Hello, " << std::endl;
        return AnalysisControl::CONTINUE;
    }
};

class World : public IAnalyzer
{
public:
    AnalysisControl OnStartActivity(
        const EventStack& eventStack) override
    {
        std::cout << "world!" << std::endl;
        return AnalysisControl::CONTINUE;
    }
};

int main()
{
    Hello hello;
    World world;

    // Let's make Hello the first analyzer in the group
    // so that it receives events and prints "Hello, "
    // first.
    auto group = MakeStaticAnalyzerGroup(&hello, &world);

    unsigned numberOfAnalysisPasses = 1;

    // Calling this function initiates the analysis and
    // forwards all events from "inputTrace.etl" to my analyzer
    // group.
    Analyze("inputTrace.etl", numberOfAnalysisPasses, group);

    return 0;
}

イベントの使用

機能 C++ API C API メモ
イベントの照合とフィルター処理 MatchEventStackInMemberFunction
MatchEventStack
MatchEventInMemberFunction
MatchEvent
C++ API には、注意を要するイベントをトレースから簡単に抽出できる関数が用意されています。 C API では、このフィルター処理を手動で行う必要があります。
イベント データ型 アクティビティ
BackEndPass
BottomUp
C1DLL
C2DLL
CodeGeneration
CommandLine
Compiler
CompilerPass
EnvironmentVariable
イベント
EventGroup
EventStack
ExecutableImageOutput
ExpOutput
FileInput
FileOutput
ForceInlinee
FrontEndFile
FrontEndFileGroup
FrontEndPass
Function
HeaderUnit
ImpLibOutput
[呼び出し]
InvocationGroup
LibOutput
リンカー
LinkerGroup
LinkerPass
LTCG
Module
ObjOutput
OptICF
OptLBR
OptRef
Pass1
Pass2
PrecompiledHeader
PreLTCGOptRef
SimpleEvent
SymbolName
TemplateInstantiation
TemplateInstantiationGroup
スレッド
TopDown
TraceInfo
TranslationUnitType
WholeProgramAnalysis
CL_PASS_DATA
EVENT_COLLECTION_DATA
EVENT_DATA
EVENT_ID
FILE_DATA
FILE_TYPE_CODE
FRONT_END_FILE_DATA
FUNCTION_DATA
FUNCTION_FORCE_INLINEE_DATA
INVOCATION_DATA
INVOCATION_VERSION_DATA
MSVC_TOOL_CODE
NAME_VALUE_PAIR_DATA
SYMBOL_NAME_DATA
TEMPLATE_INSTANTIATION_DATA
TEMPLATE_INSTANTIATION_KIND_CODE
TRACE_INFO_DATA
TRANSLATION_UNIT_PASS_CODE
TRANSLATION_UNIT_TYPE
TRANSLATION_UNIT_TYPE_DATA

アクティビティと簡易イベント

発生するイベントには、"アクティビティ" と "簡易イベント" の 2 つのカテゴリがあります。 アクティビティは継続的なプロセスであり、時間に始まりと終わりがあります。 簡易イベントは時間どおりに発生し、継続時間はありません。 C++ Build Insights SDK を使用して MSVC トレースを分析する場合、アクティビティが開始したときと停止したときに個別のイベントを受け取ります。 簡易イベントの場合、発生時に 1 つのイベントのみを受け取ります。

親子関係

アクティビティおよび簡易イベントは、親子関係を介して相互に関連付けられます。 アクティビティまたは簡易イベントの親は包括的なアクティビティであり、アクティビティまたは簡易イベントはその中で発生します。 たとえば、ソース ファイルをコンパイルする場合、コンパイラでは、ファイルを解析し、その後コードを生成する必要があります。 解析とコード生成のアクティビティは両方とも、コンパイラ アクティビティの子です。

簡易イベントに継続時間はないので、その中で他のイベントは発生しません。 したがって、それらに子はありません。

各アクティビティおよび簡易イベントの親子関係は、イベント テーブル に示されます。 C++ Build Insights イベントを使用する場合、これらの関係を把握することが重要です。 多くの場合、イベントの完全なコンテキストを理解するには、それらに依存する必要があります。

Properties

すべてのイベントには次のプロパティがあります。

プロパティ 説明
種類識別子 イベントの種類を一意に識別する数字。
インスタンス識別子 トレース内のイベントを一意に識別する数字。 トレース内で同じ種類の 2 つのイベントが発生した場合、両方とも一意のインスタンス識別子を取得します。
開始時刻 アクティビティが開始した時刻、または簡易イベントが発生した時刻。
プロセス識別子 イベントが発生したプロセスを識別する数字。
スレッド識別子 イベントが発生したスレッドを識別する数字。
プロセッサ インデックス イベントを生成した論理プロセッサを示す、0 から始まるインデックス。
イベント名 イベントの種類を説明する文字列。

簡易イベント以外のすべてのアクティビティには、次のプロパティもあります。

プロパティ 説明
Stop time アクティビティが停止した時刻。
排他継続時間 アクティビティに費やされた時間。子アクティビティに費やされた時間は含まれません。
CPU 時間 アクティビティに接続されたスレッド内でコードの実行に費やされた CPU 時間。 アクティビティに接続されたスレッドがスリープ状態だった時間は含まれません。
排他 CPU 時間 CPU 時間と同じです。ただし、子アクティビティによって費やされた CPU 時間は含まれません。
ウォール クロック時間責任 ウォール クロック時間全体に対するアクティビティの影響。 ウォール クロック時間責任では、アクティビティ間の並列処理が考慮されます。 たとえば、2 つの関連のないアクティビティが並行して実行されているとしましょう。 どちらの継続時間も 10 秒で、開始時刻と終了時刻はまったく同じです。 この場合、Build Insights では、両方に 5 秒のウォール クロック時間責任を割り当てます。 これに対して、これらのアクティビティが重複することなく順に実行される場合、両方に 10 秒のウォール クロック時間責任が割り当てられます。
排他的ウォール クロック時間責任 ウォール クロック時間責任と同じですが、子アクティビティのウォール クロック時間責任は含まれません。

一部のイベントには、前述以外の独自のプロパティがあります。 この場合、これらの追加プロパティは、イベント テーブルに一覧表示されます。

C++ Build Insights SDK で提供されるイベントの使用

イベント スタック

C++ Build Insights SDK では、イベントを提供する場合、必ずスタックの形式で提供します。 スタック内の最後のエントリは現在のイベントであり、それより前のエントリは、その親階層です。 たとえば、LTCG の開始および停止イベントは、リンカーのパス 1 で発生するとします。 この場合、受け取るスタックには、[LINKER, PASS1, LTCG] が含まれます。 親階層は、イベントをそのルートまでトレースできるので便利です。 前述の LTCG アクティビティの速度が遅い場合、どのリンカー呼び出しが関係していたかをすぐに知ることができます。

イベントとイベント スタックの照合

C++ Build Insights SDK では、トレース内のすべてのイベントが提供されますが、ほとんどの場合、注意を要するのはそれらのサブセットのみです。 場合によっては、注意を要するのは、"イベント スタック" のサブセットのみであることもあります。 SDK では、必要なイベントまたはイベント スタックをすばやく抽出して、不要なイベントを拒否するのに役立つ機能が提供されます。 これは、次の照合関数を使用して実行されます。

機能 説明
MatchEvent イベントが指定された種類のいずれかと一致する場合、それを保持します。 一致するイベントをラムダまたはその他の呼び出し可能な型に転送します。 この関数では、イベントの親階層は考慮されません。
MatchEventInMemberFunction イベントがメンバー関数のパラメーターで指定された種類と一致する場合、それを保持します。 一致するイベントをメンバー関数に転送します。 この関数では、イベントの親階層は考慮されません。
MatchEventStack イベントとその親階層の両方が指定された種類と一致する場合、そのイベントを保持します。 イベントおよび一致する親階層イベントをラムダまたはその他の呼び出し可能な型に転送します。
MatchEventStackInMemberFunction イベントとその親階層の両方がメンバー関数のパラメーター リストで指定された種類と一致する場合、そのイベントを保持します。 イベントおよび一致する親階層イベントをメンバー関数に転送します。

MatchEventStack などのイベント スタック照合関数では、親階層を照合するときにギャップを許容します。 たとえば、[LINKER, LTCG] スタックに関心があるとします。 これは、[LINKER, PASS1, LTCG] スタックとも一致します。 最後に指定する種類は、照合するイベントの種類である必要があり、親階層には含まれません。

キャプチャ クラス

Match* 関数を使用するには、照合する種類を指定する必要があります。 これらの種類は、"キャプチャ クラス" の一覧から選択されます。 キャプチャ クラスには、次に示す複数のカテゴリがあります。

カテゴリ 説明
完全一致 これらのキャプチャ クラスは、特定のイベントの種類と照合するためにのみ使用されます。 1 つの例として、Compiler があります。これは、COMPILER イベントと一致します。
ワイルドカード これらのキャプチャ クラスは、サポートされるイベントの一覧にあるいずれかのイベントと照合するために使用できます。 たとえば、Activity ワイルドカードは、任意のアクティビティ イベントと一致します。 もう 1 つの例は、CompilerPass ワイルドカードです。これは、FRONT_END_PASS または BACK_END_PASS イベントのいずれかと一致します。
グループ グループ キャプチャ クラスの名前は、Group で終わります。 これらは、ギャップを無視して、同じ種類の複数のイベントを連続して照合するために使用されます。 イベント スタック内に存在する個数がわからないため、再帰イベントを照合する場合にのみ意味があります。 たとえば、FRONT_END_FILE アクティビティは、コンパイラでファイルを解析するたびに発生します。 ファイルの解析中にコンパイラによってインクルード ディレクティブが検出される可能性があるため、このアクティビティは再帰的です。 FrontEndFile クラスは、スタック内の 1 つの FRONT_END_FILE イベントとのみ一致します。 インクルード階層全体を一致させるには、FrontEndFileGroup クラスを使用します。
ワイルドカード グループ ワイルドカード グループは、ワイルドカードとグループのプロパティを組み合わせたものです。 このカテゴリのクラスは、InvocationGroupのみです。これは、単一のイベント スタック内にあるすべての LINKER および COMPILER イベントを照合し、キャプチャします。

各イベントの照合に使用できるキャプチャ クラスについては、「イベント テーブル」を参照してください。

照合後: キャプチャされたイベントの使用

照合が正常に完了すると、Match* 関数によって、キャプチャ クラス オブジェクトが構築され、指定された関数に転送されます。 これらのキャプチャ クラス オブジェクトを使用して、イベントのプロパティにアクセスします。

AnalysisControl MyAnalyzer::OnStartActivity(const EventStack& eventStack)
{
    // The event types to match are specified in the PrintIncludes function
    // signature.  
    MatchEventStackInMemberFunction(eventStack, this, &MyAnalyzer::PrintIncludes);
}

// We want to capture event stacks where:
// 1. The current event is a FrontEndFile activity.
// 2. The current FrontEndFile activity has at least one parent FrontEndFile activity
//    and possibly many.
void PrintIncludes(FrontEndFileGroup parentIncludes, FrontEndFile currentFile)
{
    // Once we reach this point, the event stack we are interested in has been matched.
    // The current FrontEndFile activity has been captured into 'currentFile', and
    // its entire inclusion hierarchy has been captured in 'parentIncludes'.

    cout << "The current file being parsed is: " << currentFile.Path() << endl;
    cout << "This file was reached through the following inclusions:" << endl;

    for (auto& f : parentIncludes)
    {
        cout << f.Path() << endl;
    }
}