Редагувати

Поділитися через


C++ Build Insights SDK

The C++ Build Insights SDK is compatible with Visual Studio 2017 and later. To see the documentation for these versions, set the Visual Studio Version selector control for this article to Visual Studio 2017 or later. It's found at the top of the table of contents on this page.

The C++ Build Insights SDK is a collection of APIs that allow you to create personalized tools on top of the C++ Build Insights platform. This page provides a high-level overview to help you get started.

Obtaining the SDK

You can download the C++ Build Insights SDK as a NuGet package by following these steps:

  1. From Visual Studio 2017 and above, create a new C++ project.
  2. In the Solution Explorer pane, right-click on your project.
  3. Select Manage NuGet Packages from the context menu.
  4. At the top right, select the nuget.org package source.
  5. Search for the latest version of the Microsoft.Cpp.BuildInsights package.
  6. Choose Install.
  7. Accept the license.

Read on for information about the general concepts surrounding the SDK. You can also access the official C++ Build Insights samples GitHub repository to see examples of real C++ applications that use the SDK.

Collecting a trace

Using the C++ Build Insights SDK to analyze events coming out of the MSVC toolchain requires that you first collect a trace. The SDK makes use of Event Tracing for Windows (ETW) as the underlying tracing technology. Collecting a trace can be done in two ways:

Method 1: using vcperf in Visual Studio 2019 and above

  1. Open an elevated x64 Native Tools Command Prompt for VS 2019.

  2. Run the following command: vcperf /start MySessionName

  3. Build your project.

  4. Run the following command: vcperf /stopnoanalyze MySessionName outputTraceFile.etl

    Important

    Use the /stopnoanalyze command when stopping your trace with vcperf. You can't use the C++ Build Insights SDK to analyze traces stopped by the regular /stop command.

Method 2: programmatically

Use any of these C++ Build Insights SDK trace collection functions to start and stop traces programmatically. The program that executes these function calls must have administrative privileges. Only the start and stop tracing functions require administrative privileges. All other functions in the C++ Build Insights SDK can be executed without them.

Functionality C++ API C API
Starting a trace StartTracingSession StartTracingSessionA
StartTracingSessionW
Stopping a trace StopTracingSession StopTracingSessionA
StopTracingSessionW
Stopping a trace and
immediately analyzing the result
StopAndAnalyzeTracingSession StopAndAnalyzeTracingSessionA
StopAndAnalyzeTracingSession
Stopping a trace and
immediately relogging the result
StopAndRelogTracingSession StopAndRelogTracingSessionA
StopAndRelogTracingSessionW

The sections that follow show you how to configure an analysis or a relogging session. It's required for the combined functionality functions, such as StopAndAnalyzeTracingSession.

Consuming a trace

Once you have an ETW trace, use the C++ Build Insights SDK to unpack it. The SDK gives you the events in a format that allows you to develop your tools quickly. We don't recommend you consume the raw ETW trace without using the SDK. The event format used by MSVC is undocumented, optimized to scale to huge builds, and hard to make sense of. Additionally, the C++ Build Insights SDK API is stable, while the raw ETW trace format is subject to change without notice.

Functionality C++ API C API Notes
Setting up event callbacks IAnalyzer
IRelogger
ANALYSIS_CALLBACKS
RELOG_CALLBACKS
The C++ Build Insights SDK provides events through callback functions. In C++, implement the callback functions by creating an analyzer or relogger class that inherits the IAnalyzer or IRelogger interface. In C, implement the callbacks in global functions and provide pointers to them in the ANALYSIS_CALLBACKS or RELOG_CALLBACKS structure.
Building groups MakeStaticAnalyzerGroup
MakeStaticReloggerGroup
MakeDynamicAnalyzerGroup
MakeDynamicReloggerGroup
The C++ API provides helper functions and types to group multiple analyzer and relogger objects together. Groups are a neat way to divide a complex analysis into simpler steps. vcperf is organized in this way.
Analyzing or relogging Analyze
Relog
AnalyzeA
AnalyzeW
RelogA
RelogW

Analyzing and relogging

Consuming a trace is done through either an analysis session or a relogging session.

Using a regular analysis is appropriate for most scenarios. This method gives you the flexibility to choose your output format: printf text, xml, JSON, database, REST calls, and so on.

Relogging is for special-purpose analyses that need to produce an ETW output file. Using relogging, you can translate the C++ Build Insights events into your own ETW event format. An appropriate use of relogging would be to hook C++ Build Insights data to your existing ETW tools and infrastructure. For example, vcperf makes use of the relogging interfaces. That's because it must produce data the Windows Performance Analyzer, an ETW tool, can understand. Some prior knowledge of how ETW works is required if you plan on using the relogging interfaces.

Creating analyzer groups

It's important to know how to create groups. Here's an example that shows how to create an analyzer group that prints Hello, world! for every activity start event it receives.

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;
}

Using events

Functionality C++ API C API Notes
Matching and filtering events MatchEventStackInMemberFunction
MatchEventStack
MatchEventInMemberFunction
MatchEvent
The C++ API offers functions that make it easy to extract the events you care about from your traces. With the C API, this filtering must be done by hand.
Event data types Activity
BackEndPass
BottomUp
C1DLL
C2DLL
CodeGeneration
CommandLine
Compiler
CompilerPass
EnvironmentVariable
Event
EventGroup
EventStack
ExecutableImageOutput
ExpOutput
FileInput
FileOutput
ForceInlinee
FrontEndFile
FrontEndFileGroup
FrontEndPass
Function
HeaderUnit
ImpLibOutput
Invocation
InvocationGroup
LibOutput
Linker
LinkerGroup
LinkerPass
LTCG
Module
ObjOutput
OptICF
OptLBR
OptRef
Pass1
Pass2
PrecompiledHeader
PreLTCGOptRef
SimpleEvent
SymbolName
TemplateInstantiation
TemplateInstantiationGroup
Thread
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

Activities and simple events

Events come in two categories: activities and simple events. Activities are ongoing processes in time that have a beginning and an end. Simple events are punctual occurrences and don't have a duration. When analyzing MSVC traces with the C++ Build Insights SDK, you'll receive separate events when an activity starts and stops. You'll receive only one event when a simple event occurs.

Parent-child relationships

Activities and simple events are related to each other via parent-child relationships. The parent of an activity or simple event is the encompassing activity in which they occur. For example, when compiling a source file the compiler has to parse the file, then generate the code. The parsing and code generation activities are both children of the compiler activity.

Simple events don't have a duration, so nothing else can happen inside them. As such, they never have any children.

The parent-child relationships of each activity and simple event are indicated in the event table. Knowing these relationships is important when consuming C++ Build Insights events. You'll often have to rely on them to understand the full context of an event.

Properties

All events have the following properties:

Property Description
Type identifier A number that uniquely identifies the event type.
Instance identifier A number that uniquely identifies the event within the trace. If two events of the same type occur in a trace, both get a unique instance identifier.
Start time The time when an activity started, or the time when a simple event occurred.
Process identifier A number that identifies the process in which the event occurred.
Thread identifier A number that identifies the thread in which the event occurred.
Processor index A zero-based index indicating which logical processor the event was emitted by.
Event name A string that describes the event type.

All activities other than simple events also have these properties:

Property Description
Stop time The time when the activity stopped.
Exclusive duration The time spent in an activity, excluding the time spent in its child activities.
CPU time The time that the CPU spent executing code in the thread attached to the activity. It doesn't include time when the thread attached to the activity was sleeping.
Exclusive CPU time Same as CPU time, but excluding the CPU time spent by child activities.
Wall-clock time responsibility The activity's contribution to overall wall-clock time. Wall-clock time responsibility takes into account parallelism between activities. For example, let's assume two unrelated activities run in parallel. Both have a duration of 10 seconds, and exactly the same start and stop time. In this case, Build Insights assigns both a wall-clock time responsibility of 5 seconds. In contrast, if these activities run one after the other with no overlap, they're both assigned a wall-clock time responsibility of 10 seconds.
Exclusive wall-clock time responsibility Same as wall-clock time responsibility, but excludes the wall-clock time responsibility of child activities.

Some events have their own properties beyond the ones mentioned. In this case, these additional properties are listed in the event table.

Consuming events provided by the C++ Build Insights SDK

The event stack

Whenever the C++ Build Insights SDK gives you an event, it comes in the form of a stack. The last entry in the stack is the current event, and entries before it are its parent hierarchy. For example, LTCG start and stop events occur during pass 1 of the linker. In this case, the stack you'd receive contains: [LINKER, PASS1, LTCG]. The parent hierarchy is convenient because you can trace back an event to its root. If the LTCG activity mentioned above is slow, you can immediately learn which linker invocation was involved.

Matching events and event stacks

The C++ Build Insights SDK gives you every event in a trace, but most of the time you only care about a subset of them. In some cases, you may only care about a subset of event stacks. The SDK provides facilities to help you quickly extract the events or event stack you need, and reject the ones you don't. It's done through these matching functions:

Function Description
MatchEvent Keep an event if it matches one of the specified types. Forward matched events to a lambda or other callable type. The event's parent hierarchy isn't considered by this function.
MatchEventInMemberFunction Keep an event if it matches the type specified in a member function's parameter. Forward matched events to the member function. The event's parent hierarchy isn't considered by this function.
MatchEventStack Keep an event if both the event and its parent hierarchy match the types specified. Forward the event and the matched parent hierarchy events to a lambda or other callable type.
MatchEventStackInMemberFunction Keep an event if both the event and its parent hierarchy match the types specified in a member function's parameter list. Forward the event and the matched parent hierarchy events to the member function.

The event stack matching functions like MatchEventStack allow gaps when describing the parent hierarchy to match. For example, you can say you're interested in the [LINKER, LTCG] stack. It would also match the [LINKER, PASS1, LTCG] stack. The last type specified must be the event type to match, and isn't part of the parent hierarchy.

Capture classes

Using the Match* functions requires that you specify the types you want to match. These types are selected from a list of capture classes. Capture classes come in several categories, described below.

Category Description
Exact These capture classes are used to match a specific event type and none other. An example is the Compiler class, which matches the COMPILER event.
Wildcard These capture classes can be used to match any event from the list of events they support. For example, the Activity wildcard matches any activity event. Another example is the CompilerPass wildcard, which can match either the FRONT_END_PASS or the BACK_END_PASS event.
Group The names of group capture classes end in Group. They're used to match multiple events of the same type in a row, ignoring gaps. They only make sense when matching recursive events, because you don't know how many exist in the event stack. For example, the FRONT_END_FILE activity happens every time the compiler parses a file. This activity is recursive because the compiler may find an include directive while it's parsing the file. The FrontEndFile class matches only one FRONT_END_FILE event in the stack. Use the FrontEndFileGroup class to match the entire include hierarchy.
Wildcard group A wildcard group combines the properties of wildcards and groups. The only class of this category is InvocationGroup, which match and capture all LINKER and COMPILER events in a single event stack.

Refer to the event table to learn which capture classes can be used to match each event.

After matching: using captured events

Once a match completes successfully, the Match* functions construct the capture class objects and forward them to the specified function. Use these capture class objects to access the events' properties.

Example

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;
    }
}