C++ Build Insights SDK

Das C++ Build Insights SDK ist mit Visual Studio 2017 und höher kompatibel. Um die Dokumentation für diese Versionen anzuzeigen, legen Sie das Auswahlsteuerelement Version in Visual Studio für diesen Artikel auf Visual Studio 2017 oder höher fest. Es befindet sich am Anfang des Inhaltsverzeichnisses auf dieser Seite.

Das C++ Build Insights SDK ist eine Sammlung von APIs, die Ihnen ermöglichen, personalisierte Tools auf der C++ Build Insights-Plattform zu erstellen. Auf dieser Seite finden Sie eine allgemeine Übersicht, die Ihnen den Einstieg erleichtern soll.

Abrufen des SDK

Sie können das C++ Build Insights SDK mit folgenden Schritten als NuGet-Paket herunterladen:

  1. Erstellen Sie in Visual Studio 2017 und höher ein neues C++-Projekt.
  2. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf Ihr Projekt.
  3. Wählen Sie im Kontextmenü NuGet-Pakete verwalten aus.
  4. Wählen Sie oben rechts die nuget.org-Paketquelle aus.
  5. Suchen Sie nach der neuesten Version des Pakets Microsoft.Cpp.BuildInsights.
  6. Wählen Sie Installieren aus.
  7. Akzeptieren Sie die Lizenzbedingungen.

Lesen Sie die weiteren Informationen zu den allgemeinen Konzepten im Zusammenhang mit dem SDK. Sie können auch auf das offizielle GitHub-Repository mit C++ Build Insights-Beispielen zugreifen, um Beispiele echter C++-Anwendungen anzuzeigen, die das SDK verwenden.

Erfassen einer Ablaufverfolgung

Wenn Sie mit dem C++ Build Insights SDK Ereignisse aus der MSVC-Toolkette analysieren, müssen Sie zuerst eine Ablaufverfolgung erfassen. Das SDK nutzt die Ereignisablaufverfolgung für Windows (Event Tracing for Windows, ETW) als grundlegende Ablaufverfolgungstechnologie. Das Erfassen einer Ablaufverfolgung kann auf zwei Arten erfolgen:

Methode 1: Verwenden von vcperf in Visual Studio 2019 und höher

  1. Öffnen einer x64 Native Tools-Eingabeaufforderung mit erhöhten Rechten für VS 2019.

  2. Führen Sie den folgenden Befehl aus: vcperf /start MySessionName

  3. Erstellen Sie das Projekt.

  4. Führen Sie den folgenden Befehl aus: vcperf /stopnoanalyze MySessionName outputTraceFile.etl

    Wichtig

    Beenden Sie die Ablaufverfolgung mit vcperf mit dem /stopnoanalyze-Befehl. Sie können mit dem C++ Build Insights SDK keine Ablaufverfolgungen analysieren, die mit dem regulären /stop-Befehl beendet wurden.

Methode 2: programmgesteuert

Verwenden Sie eine dieser C++ Build Insights SDK-Funktionen zum Erfassen von Ablaufverfolgungen, um Ablaufverfolgungen programmgesteuert zu starten und zu beenden. Das Programm, das diese Funktionsaufrufe ausführt, muss über Administratorrechte verfügen. Nur die Funktionen zum Starten und Beenden von Ablaufverfolgungsfunktionen erfordern Administratorrechte. Alle anderen Funktionen im C++ Build Insights SDK können ohne sie ausgeführt werden.

Funktionalität C++-API C-API
Starten einer Ablaufverfolgung StartTracingSession StartTracingSessionA
StartTracingSessionW
Beenden einer Ablaufverfolgung StopTracingSession StopTracingSessionA
StopTracingSessionW
Beenden einer Ablaufverfolgung und
sofortiges Analysieren des Ergebnisses
StopAndAnalyzeTracingSession StopAndAnalyzeTracingSessionA
StopAndAnalyzeTracingSession
Beenden einer Ablaufverfolgung und
sofortige Neuprotokollierung des Ergebnisses
StopAndRelogTracingSession StopAndRelogTracingSessionA
StopAndRelogTracingSessionW

In den folgenden Abschnitten wird gezeigt, wie Sie eine Analyse- oder eine Neuprotokollierungssitzung konfigurieren. Dies ist für die Funktionen mit kombinierter Funktionalität wie StopAndAnalyzeTracingSession erforderlich.

Nutzen einer Ablaufverfolgung

Wenn Sie über eine ETW-Ablaufverfolgung verfügen, entpacken Sie sie mit dem C++ Build Insights SDK. Das SDK stellt Ihnen die Ereignisse in einem Format bereit, mit dem Sie Ihre Tools schnell entwickeln können. Sie sollten die ETW-Ablaufverfolgung nicht ohne das SDK verwenden. Das von MSVC verwendete Ereignisformat ist undokumentiert, zur Skalierung für umfangreiche Builds optimiert und schwer zu verstehen. Außerdem ist die C++ Build Insights SDK-API beständig, während das reine ETW-Format ohne vorherige Ankündigung geändert werden kann.

Funktionalität C++-API C-API Hinweise
Einrichten von Ereignisrückrufen IAnalyzer
IRelogger
ANALYSIS_CALLBACKS
RELOG_CALLBACKS
Das C++ Build Insights SDK stellt Ereignisse mithilfe von Rückruffunktionen bereit. Implementieren Sie in C++ die Rückruffunktionen, indem Sie eine Analysetool- oder Reloggerklasse erstellen, die die IAnalyzer- oder IRelogger-Schnittstelle erbt. Implementieren Sie in C die Rückrufe in globalen Funktionen, und stellen Sie in der ANALYSIS_CALLBACKS- oder RELOG_CALLBACKS-Struktur Zeiger darauf bereit.
Erstellen von Gruppen MakeStaticAnalyzerGroup
MakeStaticReloggerGroup
MakeDynamicAnalyzerGroup
MakeDynamicReloggerGroup
Die C++-API stellt Hilfsfunktionen und -typen bereit, um mehrere Analysetool- und Reloggerobjekte zu gruppieren. Gruppen sind eine gute Möglichkeit, eine komplexe Analyse in einfachere Schritte aufzuteilen. vcperf ist auf diese Weise organisiert.
Analysieren oder erneut protokollieren Analysieren.
Relog
AnalyzeA
AnalyzeW
ErneutesLoga
NeulogW

Analysieren und erneut protokollieren

Die Nutzung einer Ablaufverfolgung erfolgt entweder über eine Analyse- oder eine Neuprotokollierungssitzung.

Die Verwendung einer regulären Analyse ist für die meisten Szenarien geeignet. Diese Methode bietet Ihnen die Flexibilität, das Ausgabeformat auszuwählen: printf-Text, XML, JSON, Datenbank, REST-Aufrufe usw.

Die Neuprotokollierung ist für zweckgebundene Analysen vorgesehen, die eine ETW-Ausgabedatei produzieren müssen. Mithilfe der Neuprotokollierung können Sie die C++ Build Insights-Ereignisse in Ihr eigenes ETW-Ereignisformat übersetzen. Eine geeignete Verwendung der Neuprotokollierung wäre, die C++ Build Insights-Daten in Ihre vorhandenen ETW-Tools und Ihre Infrastruktur einzubinden. Beispielsweise verwendet vcperf die Schnittstellen der Neuprotokollierung. Das liegt daran, dass Daten erzeugt werden müssen, die der Windows Performance Analyzer – ein ETW-Tool – versteht. Wenn Sie die Schnittstellen der Neuprotokollierung verwenden möchten, benötigen Sie Vorkenntnisse der Funktionsweise von ETW.

Erstellen von Analysetoolgruppen

Sie müssen wissen, wie Gruppen erstellt werden. Hier ist ein Beispiel, das zeigt, wie Sie eine Analysegruppe erstellen, die Hello, world! für jedes empfangene Aktivitätsstartereignis druckt.

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

Verwenden von Ereignissen

Funktionalität C++-API C-API Hinweise
Abgleichen und Filtern von Ereignissen MatchEventStackInMemberFunction
MatchEventStack
MatchEventInMemberFunction
MatchEvent
Die C++-API bietet Funktionen, die Ihnen erleichtern, die Ereignisse aus Ihren Ablaufverfolgungen zu extrahieren, die Sie interessieren. Bei der C-API muss diese Filterung manuell erfolgen.
Ereignisdatentypen Aktivität
BackEndPass
BottomUp
C1DLL
C2DLL
CodeGeneration
CommandLine
Compiler
CompilerPass
EnvironmentVariable
Event
EventGroup
EventStack
ExecutableImageOutput
ExpOutput
FileInput
FileOutput
ForceInlinee
FrontEndFile
FrontEndFileGroup
FrontEndPass
Function
HeaderUnit
ImpLibOutput
Aufruf
InvocationGroup
LibOutput
Linker
LinkerGroup
LinkerPass
LTCG
Modul
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

Aktivitäten und einfache Ereignisse

Ereignisse lassen sich in zwei Kategorien einteilen: Aktivitäten und einfache Ereignisse. Aktivitäten sind in einem Zeitraum ablaufende Prozesse mit Anfang und Ende. Einfache Ereignisse sind punktuelle Vorkommnisse ohne Dauer. Beim Analysieren von MSVC-Ablaufverfolgungen mit dem C++ Build Insights SDK erhalten Sie separate Ereignisse, wenn eine Aktivität startet und endet. Sie erhalten nur ein Ereignis, wenn ein einfaches Ereignis auftritt.

Beziehungen zwischen übergeordneten und untergeordneten Elementen

Aktivitäten und einfache Ereignisse sind über Beziehungen zwischen übergeordneten und untergeordneten Elementen miteinander verknüpft. Das übergeordnete Element von Aktivitäten oder einfachen Ereignissen ist die umfassende Aktivität, in der sie auftreten. Wenn Sie z. B. eine Quelldatei kompilieren, muss der Compiler die Datei analysieren und dann den Code generieren. Die Aktivitäten für Analyse und Codegenerierung sind beide untergeordnete Elemente der Compileraktivität.

Da einfache Ereignisse keine Dauer haben, kann innerhalb einfacher Ereignisse nichts anderes geschehen. Folglich haben sie niemals untergeordnete Elemente.

Die Beziehungen zwischen übergeordneten und untergeordneten Elementen der einzelnen Aktivitäten und einfachen Ereignisse werden in der Ereignistabelle angegeben. Wenn Sie C++ Build Insights-Ereignisse nutzen, müssen Sie diese Beziehungen kennen. Häufig müssen Sie sich auf sie verlassen, um den vollständigen Kontext eines Ereignisses zu verstehen.

Eigenschaften

Alle Ereignisse verfügen über folgende Eigenschaften:

Eigenschaft Beschreibung
Typbezeichner Eine Zahl, die den Ereignistyp eindeutig identifiziert.
Instanzbezeichner Eine Zahl, die das Ereignis innerhalb der Ablaufverfolgung eindeutig identifiziert. Wenn zwei Ereignisse desselben Typs in einer Ablaufverfolgung auftreten, erhalten beide einen eindeutigen Instanzbezeichner.
Startzeit Der Zeitpunkt, zu dem eine Aktivität gestartet wurde, oder der Zeitpunkt, zu dem ein einfaches Ereignis auftrat.
Prozessbezeichner Eine Zahl, die den Prozess identifiziert, in dem das Ereignis auftrat.
Threadbezeichner Eine Zahl, die den Thread identifiziert, in dem das Ereignis auftrat.
Prozessorindex Ein auf 0 (null) basierender Index, der den logischen Prozessor angibt, von dem das Ereignis ausgegeben wurde.
Event name (Ereignisname) Eine Zeichenfolge, die den Ereignistyp beschreibt.

Alle Aktivitäten, die keine einfachen Ereignisse sind, haben auch folgende Eigenschaften:

Eigenschaft Beschreibung
Endzeit Der Zeitpunkt, zu dem die Aktivität beendet wurde.
Exklusive Dauer Die für eine Aktivität aufgewendete Zeit mit Ausnahme der in den untergeordneten Aktivitäten aufgewendeten Zeit.
CPU-Zeit Die Zeit, die die CPU für das Ausführen von Code in dem der Aktivität angefügten Thread aufgewendet hat. Die Zeit, für die der der Aktivität angefügte Thread sich im Ruhezustand befand, ist darin nicht enthalten.
Exklusive CPU-Zeit CPU-Zeit ohne die für untergeordnete Aktivitäten aufgewendete CPU-Zeit.
Gesamtbetrachtungszeit-Verantwortung Der Beitrag der Aktivität zur Gesamtbetrachtungszeit. Die Gesamtbetrachtungszeit berücksichtigt die Parallelität zwischen Aktivitäten. Nehmen wir beispielsweise an, dass zwei nicht verknüpfte Aktivitäten parallel ausgeführt werden. Beide haben eine Dauer von 10 Sekunden und genau dieselbe Start- und Endzeit. In diesem Fall weist Build Insights beiden eine Gesamtbetrachtungszeit-Verantwortung von 5 Sekunden zu. Wenn diese Aktivitäten dagegen nacheinander und ohne Überlappung ausgeführt werden, wird ihnen jeweils eine Gesamtbetrachtungszeit-Verantwortung von 10 Sekunden zugewiesen.
Exklusive Gesamtbetrachtungszeit-Verantwortung Gesamtbetrachtungszeit-Verantwortung mit Ausnahme der Gesamtbetrachtungszeit-Verantwortung untergeordneter Aktivitäten.

Einige Ereignisse haben neben den erwähnten Eigenschaften eigene Eigenschaften. In diesem Fall werden diese zusätzlichen Eigenschaften in der Ereignistabelle aufgelistet.

Nutzen von Ereignissen, die vom C++ Build Insights SDK bereitgestellt werden

Der Ereignisstapel

Immer dann, wenn das C++ Build Insights SDK ein Ereignis meldet, wird es in Form eines Stapels bereitgestellt. Der letzte Eintrag im Stapel ist das aktuelle Ereignis, und die Einträge davor sind ihm übergeordnet. Beispielsweise treten das LTCG-Start- und Endeereignis während des ersten Linkerdurchlaufs auf. In diesem Fall enthält der empfangene Stapel: [LINKER, PASS1, LTCG]. Die übergeordnete Hierarchie ist praktisch, da Sie so ein Ereignis auf seine Ursache zurückverfolgen können. Wenn die oben genannte LTCG-Aktivität langsam ist, können Sie sofort ermitteln, welcher Linkeraufruf beteiligt war.

Abgleichen von Ereignissen und Ereignisstapeln

Das C++ Build Insights SDK stellt Ihnen jedes Ereignis in einer Ablaufverfolgung bereit, aber in den meisten Fällen ist nur eine Teilmenge davon wichtig. In einigen Fällen ist möglicherweise nur eine Teilmenge der Ereignisstapel für Sie interessant. Mithilfe des SDK können Sie schnell die benötigten Ereignisse oder Ereignisstapel extrahieren und diejenigen ablehnen, die Sie nicht benötigen. Dies erfolgt über diese Abgleichsfunktionen:

Function Beschreibung
MatchEvent Beibehalten eines Ereignisses, wenn es mit einem der angegebenen Typen übereinstimmt. Weiterleiten übereinstimmender Ereignisse an eine Lambdafunktion oder einen anderen aufrufbaren Typ. Die übergeordnete Hierarchie des Ereignisses wird von dieser Funktion nicht berücksichtigt.
MatchEventInMemberFunction Beibehalten eines Ereignisses, wenn es mit dem in einem Parameter der Memberfunktion angegebenen Typen übereinstimmt. Weiterleiten übereinstimmender Ereignisse an eine Memberfunktion. Die übergeordnete Hierarchie des Ereignisses wird von dieser Funktion nicht berücksichtigt.
MatchEventStack Beibehalten eines Ereignisses, wenn sowohl das Ereignis als auch seine übergeordnete Hierarchie den angegebenen Typen entspricht. Weiterleiten des Ereignisses und der übereinstimmenden Ereignisse der übergeordneten Hierarchie an eine Lambdafunktion oder einen anderen aufrufbaren Typen.
MatchEventStackInMemberFunction Beibehalten eines Ereignisses, wenn sowohl das Ereignis als auch seine übergeordnete Hierarchie den in der Parameterliste einer Memberfunktion angegebenen Typen entspricht. Weiterleiten des Ereignisses und der übereinstimmenden Ereignisse der übergeordneten Hierarchie an die Memberfunktion.

Die Ereignisstapel-Abgleichsfunktionen wie MatchEventStack lassen Lücken bei der Beschreibung der abzugleichenden übergeordneten Hierarchie zu. Sie können beispielsweise sagen, dass Sie an dem Stapel [LINKER, LTCG] interessiert sind. Es würde auch mit dem Stapel [LINKER, PASS1, LTCG] übereinstimmen. Der letzte angegebene Typ muss dem Ereignistyp entsprechen, der abgeglichen werden soll, und ist nicht Teil der übergeordneten Hierarchie.

Erfassungsklassen

Die Verwendung der Match*-Funktionen erfordert, dass Sie die Typen angeben, die Sie abgleichen möchten. Diese Typen werden aus einer Liste von Erfassungsklassen ausgewählt. Erfassungsklassen sind in verschiedenen Kategorien enthalten, die unten beschrieben werden.

Category Beschreibung
Exact Diese Erfassungsklassen werden verwendet, um einen bestimmten Ereignistyp und keinen anderen abzugleichen. Ein Beispiel hierfür ist die Compiler-Klasse zum Abgleich des COMPILER-Ereignisses.
Platzhalter Diese Erfassungsklassen können verwendet werden, um beliebige Ereignisse aus der Liste der von Ihnen unterstützten Ereignisse abzugleichen. Beispielsweise entspricht der Activity-Platzhalter jedem Aktivitätsereignis. Ein weiteres Beispiel ist der CompilerPass-Platzhalter, der entweder dem FRONT_END_PASS- oder dem BACK_END_PASS-Ereignis entsprechen kann.
Group Die Namen der Gruppenerfassungsklassen enden mit Group. Sie werden verwendet, um mehrere Ereignisse desselben Typs in einer Zeile abzugleichen, wobei Lücken ignoriert werden. Sie sind nur sinnvoll, wenn rekursive Ereignisse abgeglichen werden, da Sie nicht wissen, wie viele im Ereignisstapel vorhanden sind. Beispielsweise erfolgt die FRONT_END_FILE-Aktivität jedes Mal, wenn der Compiler eine Datei analysiert. Diese Aktivität ist rekursiv, da der Compiler möglicherweise bei der Analyse der Datei eine Include-Direktive findet. Die FrontEndFile-Klasse stimmt nur mit einem FRONT_END_FILE-Ereignis im Stapel überein. Verwenden Sie die Klasse FrontEndFileGroup, um die gesamte Include-Hierarchie abzugleichen.
Platzhaltergruppe Eine Platzhaltergruppe vereinigt in sich die Eigenschaften von Platzhaltern und Gruppen. Die einzige Klasse dieser Kategorie ist InvocationGroup, die alle LINKER- und COMPILER-Ereignisse in einem einzelnen Ereignisstapel abgleicht und erfasst.

Informationen zu den Erfassungsklassen, die verwendet werden können, um die einzelnen Ereignisse abzugleichen, finden Sie in der Ereignistabelle.

Nach dem Abgleich: Verwenden von aufgezeichneten Ereignissen

Nach erfolgreichem Abschluss eines Abgleichs erstellen die Match*-Funktionen die Erfassungsklassenobjekte und leiten sie an die angegebene Funktion weiter. Verwenden Sie diese Objekte der Erfassungsklasse zum Zugriff auf die Eigenschaften der Ereignisse.

Beispiel

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