C++ Build Insights SDK
Build Insights SDK per C++ è compatibile con Visual Studio 2017 e versioni successive. Per visualizzare la documentazione per queste versioni, impostare il controllo selettore della versione di Visual Studio per questo articolo su Visual Studio 2017 o versione successiva. Si trova nella parte superiore del sommario in questa pagina.
C++ Build Insights SDK è una raccolta di API che consentono di creare strumenti personalizzati sulla piattaforma C++ Build Insights. Questa pagina offre una panoramica generale che consente di iniziare.
Ottenere l'SDK
È possibile scaricare C++ Build Insights SDK come pacchetto NuGet seguendo questa procedura:
- Da Visual Studio 2017 e versioni successive creare un nuovo progetto C++.
- Nel riquadro Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto.
- Selezionare Gestisci pacchetti NuGet dal menu di scelta rapida.
- In alto a destra selezionare l'origine del pacchetto nuget.org .
- Cercare la versione più recente del pacchetto Microsoft.Cpp.BuildInsights.
- Scegliere Installa.
- Accettare la licenza.
Leggere per informazioni sui concetti generali relativi all'SDK. È anche possibile accedere al repository GitHub ufficiale degli esempi di Build Insights per C++ per visualizzare esempi di applicazioni C++ reali che usano l'SDK.
Raccolta di una traccia
L'uso di C++ Build Insights SDK per analizzare gli eventi provenienti dalla toolchain MSVC richiede prima di tutto di raccogliere una traccia. L'SDK usa Event Tracing for Windows (ETW) come tecnologia di traccia sottostante. La raccolta di una traccia può essere eseguita in due modi:
Metodo 1: uso di vcperf in Visual Studio 2019 e versioni successive
Aprire un prompt dei comandi degli strumenti nativi x64 con privilegi elevati per VS 2019.
Eseguire il seguente comando:
vcperf /start MySessionName
Crea il progetto.
Eseguire il seguente comando:
vcperf /stopnoanalyze MySessionName outputTraceFile.etl
Importante
Usare il
/stopnoanalyze
comando quando si arresta la traccia con vcperf. Non è possibile usare C++ Build Insights SDK per analizzare le tracce arrestate dal normale/stop
comando.
Metodo 2: a livello di codice
Usare una di queste funzioni di raccolta di tracce di C++ Build Insights SDK per avviare e arrestare le tracce a livello di codice. Il programma che esegue queste chiamate di funzione deve disporre di privilegi amministrativi. Solo le funzioni di avvio e arresto della traccia richiedono privilegi amministrativi. Tutte le altre funzioni in C++ Build Insights SDK possono essere eseguite senza di esse.
Funzioni SDK correlate alla raccolta di tracce
Funzionalità | API C++ | API C |
---|---|---|
Avvio di una traccia | StartTracingSession | StartTracingSessionA StartTracingSessionW |
Arresto di una traccia | StopTracingSession | StopTracingSessionA StopTracingSessionW |
Arresto di una traccia e analisi immediata del risultato |
StopAndAnalyzeTracingSession | StopAndAnalyzeTracingSessionA StopAndAnalyzeTracingSession |
Arresto di una traccia e rilocazione immediata del risultato |
StopAndRelogTracingSession | StopAndRelogTracingSessionA StopAndRelogTracingSessionW |
Le sezioni seguenti illustrano come configurare un'analisi o una sessione di rilogging. È necessario per le funzioni combinate di funzionalità, ad esempio StopAndAnalyzeTracingSession.
Utilizzo di una traccia
Dopo aver creato una traccia ETW, usare C++ Build Insights SDK per decomprimerla. L'SDK offre gli eventi in un formato che consente di sviluppare rapidamente gli strumenti. Non è consigliabile usare la traccia ETW non elaborata senza usare l'SDK. Il formato di evento usato da MSVC non è documentato, ottimizzato per la scalabilità a build di grandi dimensioni e difficile da comprendere. Inoltre, l'API di Build Insights SDK di C++ è stabile, mentre il formato di traccia ETW non elaborato è soggetto a modifiche senza preavviso.
Tipi e funzioni SDK correlati all'utilizzo di traccia
Funzionalità | API C++ | API C | Note |
---|---|---|---|
Configurazione dei callback degli eventi | IAnalyzer IRelogger |
ANALYSIS_CALLBACKS RELOG_CALLBACKS |
L'SDK di Build Insights per C++ fornisce eventi tramite funzioni di callback. In C++, implementare le funzioni di callback creando un analizzatore o una classe relogger che eredita l'interfaccia IAnalyzer o IRelogger. In C implementare i callback nelle funzioni globali e fornire puntatori a essi nella struttura ANALYSIS_CALLBACKS o RELOG_CALLBACKS. |
Compilazione di gruppi | MakeStaticAnalyzerGroup MakeStaticReloggerGroup MakeDynamicAnalyzerGroup MakeDynamicReloggerGroup |
L'API C++ fornisce funzioni helper e tipi per raggruppare più oggetti analizzatori e rilogger. I gruppi sono un modo ordinato per dividere un'analisi complessa in passaggi più semplici. vcperf è organizzato in questo modo. | |
Analisi o rilogging | Analisi. Ripetere il log |
AnalyzeA AnalyzeW RilogA RilogW |
Analisi e rilogging
L'utilizzo di una traccia viene eseguito tramite una sessione di analisi o una sessione di rilogging.
L'uso di un'analisi regolare è appropriato per la maggior parte degli scenari. Questo metodo offre la flessibilità necessaria per scegliere il formato di output: printf
testo, xml, JSON, database, chiamate REST e così via.
Il relogging è destinato ad analisi speciali che devono produrre un file di output ETW. Usando il relogging, è possibile convertire gli eventi di C++ Build Insights nel proprio formato di evento ETW. Un uso appropriato del rilogging sarebbe quello di associare i dati di Build Insights C++ agli strumenti e all'infrastruttura ETW esistenti. Ad esempio, vcperf usa le interfacce di rilogging. Ciò è dovuto al fatto che deve produrre dati che windows analizzatore prestazioni, uno strumento ETW, può comprendere. Alcune conoscenze precedenti sul funzionamento di ETW sono necessarie se si prevede di usare le interfacce di rilogging.
Creazione di gruppi di analizzatori
È importante sapere come creare gruppi. Ecco un esempio che mostra come creare un gruppo di analizzatori che stampa Hello, world! per ogni evento di avvio dell'attività ricevuto.
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;
}
Uso degli eventi
Tipi e funzioni DELL'SDK correlati agli eventi
Attività ed eventi semplici
Gli eventi sono disponibili in due categorie: attività ed eventi semplici. Le attività sono processi in corso nel tempo che hanno un inizio e una fine. Gli eventi semplici sono occorrenze puntuali e non hanno una durata. Quando si analizzano tracce MSVC con C++ Build Insights SDK, si riceveranno eventi separati all'avvio e all'arresto di un'attività. Si riceverà un solo evento quando si verifica un evento semplice.
Relazioni padre-figlio
Le attività e gli eventi semplici sono correlati tra loro tramite relazioni padre-figlio. L'elemento padre di un'attività o di un evento semplice è l'attività che comprende in cui si verificano. Ad esempio, quando si compila un file di origine, il compilatore deve analizzare il file, quindi generare il codice. Le attività di analisi e generazione del codice sono entrambi elementi figlio dell'attività del compilatore.
Gli eventi semplici non hanno una durata, quindi nessun altro può verificarsi all'interno di essi. Di conseguenza, non hanno mai figli.
Le relazioni padre-figlio di ogni attività e evento semplice sono indicate nella tabella eventi. Conoscere queste relazioni è importante quando si usano gli eventi di Build Insights C++. Spesso è necessario basarsi su di essi per comprendere il contesto completo di un evento.
Proprietà
Tutti gli eventi hanno le proprietà seguenti:
Proprietà | Descrizione |
---|---|
Identificatore del tipo | Numero che identifica in modo univoco il tipo di evento. |
Identificatore dell'istanza | Numero che identifica in modo univoco l'evento all'interno della traccia. Se due eventi dello stesso tipo si verificano in una traccia, entrambi ottengono un identificatore di istanza univoco. |
Ora di inizio | Ora di avvio di un'attività o dell'ora in cui si è verificato un evento semplice. |
Identificatore del processo | Numero che identifica il processo in cui si è verificato l'evento. |
Identificatore del thread | Numero che identifica il thread in cui si è verificato l'evento. |
Indice processore | Indice in base zero che indica il processore logico da cui è stato generato l'evento. |
Nome evento | Stringa che descrive il tipo di evento. |
Tutte le attività diverse da eventi semplici hanno anche queste proprietà:
Proprietà | Descrizione |
---|---|
Tempo di arresto | Ora in cui l'attività è stata arrestata. |
Durata esclusiva | Tempo trascorso in un'attività, escluso il tempo trascorso nelle attività figlio. |
Tempo CPU | Tempo impiegato dalla CPU per l'esecuzione del codice nel thread collegato all'attività. Non include l'ora di sospensione del thread collegato all'attività. |
Tempo esclusivo cpu | Uguale al tempo della CPU, ma escluso il tempo di CPU impiegato dalle attività figlio. |
Responsabilità dell'ora del muro | Contributo dell'attività all'ora complessiva del tempo. La responsabilità del tempo a muro tiene conto del parallelismo tra le attività. Si supponga, ad esempio, che due attività non correlate vengano eseguite in parallelo. Entrambi hanno una durata di 10 secondi e esattamente lo stesso tempo di inizio e arresto. In questo caso, Build Insights assegna sia una responsabilità di tempo in tempo reale di 5 secondi. Al contrario, se queste attività vengono eseguite una dopo l'altra senza sovrapposizioni, a entrambi viene assegnata una responsabilità di tempo di clock a muro di 10 secondi. |
Responsabilità esclusiva del tempo a muro | Come la responsabilità del tempo a muro, ma esclude la responsabilità dell'ora del muro delle attività dei bambini. |
Alcuni eventi hanno proprietà proprie oltre quelle menzionate. In questo caso, queste proprietà aggiuntive sono elencate nella tabella eventi.
Utilizzo di eventi forniti da C++ Build Insights SDK
Stack di eventi
Ogni volta che C++ Build Insights SDK offre un evento, si presenta sotto forma di stack. L'ultima voce nello stack è l'evento corrente e le voci prima che siano la gerarchia padre. Ad esempio, gli eventi di avvio e arresto ltcg si verificano durante il passaggio 1 del linker. In questo caso, lo stack ricevuto contiene: [LINKER, PASS1, LTCG]. La gerarchia padre è utile perché è possibile ricondurre un evento alla radice. Se l'attività LTCG menzionata in precedenza è lenta, è possibile apprendere immediatamente quale chiamata del linker è stata coinvolta.
Corrispondenza di eventi e stack di eventi
C++ Build Insights SDK offre ogni evento in una traccia, ma la maggior parte del tempo è importante solo per un sottoinsieme di essi. In alcuni casi, è possibile preoccuparsi solo di un subset di stack di eventi. L'SDK offre funzionalità che consentono di estrarre rapidamente gli eventi o lo stack di eventi necessari e rifiutare quelli non necessari. Questa operazione viene eseguita tramite queste funzioni di corrispondenza:
Funzione | Descrizione |
---|---|
MatchEvent | Mantenere un evento se corrisponde a uno dei tipi specificati. Inoltrare gli eventi corrispondenti a un'espressione lambda o a un altro tipo chiamabile. La gerarchia padre dell'evento non viene considerata da questa funzione. |
MatchEventInMemberFunction | Mantenere un evento se corrisponde al tipo specificato nel parametro di una funzione membro. Inoltrare gli eventi corrispondenti alla funzione membro. La gerarchia padre dell'evento non viene considerata da questa funzione. |
MatchEventStack | Mantenere un evento se l'evento e la relativa gerarchia padre corrispondono ai tipi specificati. Inoltrare l'evento e gli eventi della gerarchia padre corrispondenti a un'espressione lambda o a un altro tipo chiamabile. |
MatchEventStackInMemberFunction | Mantenere un evento se l'evento e la relativa gerarchia padre corrispondono ai tipi specificati nell'elenco dei parametri di una funzione membro. Inoltrare l'evento e gli eventi della gerarchia padre corrispondenti alla funzione membro. |
Le funzioni di corrispondenza dello stack di eventi, ad esempio MatchEventStack
consentono lacune durante la descrizione della gerarchia padre. Ad esempio, si può dire di essere interessati allo stack [LINKER, LTCG]. Corrisponderebbe anche allo stack [LINKER, PASS1, LTCG]. L'ultimo tipo specificato deve essere il tipo di evento da trovare e non fa parte della gerarchia padre.
Classi di acquisizione
Per usare le Match*
funzioni è necessario specificare i tipi che si desidera associare. Questi tipi vengono selezionati da un elenco di classi di acquisizione. Le classi di acquisizione sono disponibili in diverse categorie, descritte di seguito.
Categoria | Descrizione |
---|---|
Exact | Queste classi di acquisizione vengono usate per trovare una corrispondenza con un tipo di evento specifico e nessun altro. Un esempio è la classe Compiler , che corrisponde all'evento COMPILER . |
Wildcard (Carattere jolly) | Queste classi di acquisizione possono essere usate per trovare le corrispondenze con qualsiasi evento dall'elenco di eventi supportati. Ad esempio, il carattere jolly Activity corrisponde a qualsiasi evento di attività. Un altro esempio è il carattere jolly CompilerPass , che può corrispondere al FRONT_END_PASS o all'evento BACK_END_PASS . |
Raggruppa | I nomi delle classi di acquisizione di gruppo terminano in Group. Vengono usati per trovare le corrispondenze con più eventi dello stesso tipo in una riga, ignorando le lacune. Hanno senso solo quando corrispondono a eventi ricorsivi, perché non si conosce il numero di eventi presenti nello stack di eventi. Ad esempio, l'attività FRONT_END_FILE viene eseguita ogni volta che il compilatore analizza un file. Questa attività è ricorsiva perché il compilatore potrebbe trovare una direttiva di inclusione durante l'analisi del file. La classe FrontEndFile corrisponde a un solo evento FRONT_END_FILE nello stack. Usare la classe FrontEndFileGroup per trovare la corrispondenza con l'intera gerarchia di inclusione. |
Gruppo con caratteri jolly | Un gruppo con caratteri jolly combina le proprietà dei caratteri jolly e dei gruppi. L'unica classe di questa categoria è InvocationGroup, che corrisponde e acquisisce tutti gli eventi LINKER e COMPILER in un singolo stack di eventi. |
Fare riferimento alla tabella eventi per informazioni sulle classi di acquisizione che è possibile usare per trovare le corrispondenze con ogni evento.
Dopo la corrispondenza: uso degli eventi acquisiti
Al termine di una corrispondenza, le Match*
funzioni costruiscono gli oggetti della classe di acquisizione e li inoltrano alla funzione specificata. Usare questi oggetti classe di acquisizione per accedere alle proprietà degli eventi.
Esempio
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;
}
}