Partilhar via


SDK do C++ Build Insights

O SDK do C++ Build Insights é compatível com o Visual Studio 2017 e posterior. Para ver a documentação dessas versões, defina o controle seletor de Versão do Visual Studio deste artigo para o Visual Studio 2017 ou posterior. Ele é encontrado na parte superior da tabela de conteúdo nesta página.

O SDK do C++ Build Insights é uma coleção de APIs que permite criar ferramentas personalizadas baseadas na plataforma C++ Build Insights. Esta página fornece uma visão geral de alto nível para ajudar você a começar.

Obter o SDK

Você pode baixar o SDK do C++ Build Insights como um pacote do NuGet seguindo estas etapas:

  1. No Visual Studio 2017 e versões superiores, crie um projeto do C++.
  2. No painel do Gerenciador de Soluções, clique com o botão direito do mouse em seu projeto.
  3. Selecione Gerenciar Pacotes do NuGet no menu de contexto.
  4. No canto superior direito, selecione o provedor de origem do pacote nuget.org.
  5. Pesquise a versão mais recente do pacote Microsoft.Cpp.BuildInsights.
  6. Escolha Instalar.
  7. Aceite a licença.

Leia mais informações sobre os conceitos gerais relativos ao SDK. Você também pode acessar o repositório oficial do GitHub de exemplos do C++ Build Insights para ver exemplos de aplicativos C++ reais que usam o SDK.

Coletando um rastreamento

Para usar o SDK do C++ Build Insights para analisar eventos que saem da cadeia de ferramentas do MSVC você precisa primeiro coletar um rastreamento. O SDK usa o ETW (Rastreamento de Eventos para Windows) como tecnologia de rastreamento subjacente. A coleta de um rastreamento pode ser feita de duas maneiras:

Método 1: usando vcperf no Visual Studio 2019 e versões superiores

  1. Abra um Prompt de Comando de Ferramentas Nativas do x64 com privilégios elevados para o VS 2019.

  2. Execute o seguinte comando: vcperf /start MySessionName

  3. Compile o projeto.

  4. Execute o seguinte comando: vcperf /stopnoanalyze MySessionName outputTraceFile.etl

    Importante

    Use o comando /stopnoanalyze ao interromper o rastreamento com o vcperf. Você não pode usar o SDK do C++ Build Insights para analisar rastreamentos interrompidos pelo comando regular /stop.

Método 2: programaticamente

Use qualquer uma dessas funções de coleta de rastreamento do SDK do Build Insights do C++ para iniciar e parar rastreamentos programaticamente. O programa que executa essas chamadas de função deve ter privilégios administrativos. Somente as funções de iniciar e parar o rastreamento exigem privilégios administrativos. Todas as outras funções no SDK do C++ Build Insights podem ser executadas sem esses privilégios.

Funcionalidade API de C++ API do C
Iniciar um rastreamento StartTracingSession StartTracingSessionA
StartTracingSessionW
Interromper um rastreamento StopTracingSession StopTracingSessionA
StopTracingSessionW
Parar um rastreamento e
analisar imediatamente o resultado
StopAndAnalyzeTracingSession StopAndAnalyzeTracingSessionA
StopAndAnalyzeTracingSession
Parar um rastreamento e
recriar imediatamente o registro do resultado
StopAndRelogTracingSession StopAndRelogTracingSessionA
StopAndRelogTracingSessionW

As seções a seguir mostram como configurar uma sessão de análise ou de recriação de registro. Ela é necessária para as funções com recursos combinados, como StopAndAnalyzeTracingSession.

Consumir um rastreamento

Após obter um rastreamento do ETW, use o SDK do C++ Build Insights para desempacotá-lo. O SDK fornece os eventos em um formato que permite que você desenvolva ferramentas rapidamente. Não recomendamos que você consuma o rastreamento do ETW bruto, sem usar o SDK. O formato de evento usado pelo MSVC não está documentado, é otimizado para escala de builds enormes e é difícil de entender. Além disso, a API do SDK do C++ Build Insights é estável, enquanto que o formato de rastreamento do ETW bruto está sujeito a alterações sem aviso prévio.

Funcionalidade API de C++ API do C Observações
Configurar retornos de chamada de evento IAnalyzer
IRelogger
ANALYSIS_CALLBACKS
RELOG_CALLBACKS
O SDK do C++ Build Insights fornece eventos por meio de funções de retorno de chamada. No C++, você implementa as funções de retorno de chamada criando uma classe de analisador ou de criação de novo registro que herda a interface IAnalyzer ou IRelogger. No C, você implementa os retornos de chamada em funções globais e fornece ponteiros para elas na estrutura ANALYSIS_CALLBACKS ou RELOG_CALLBACKS.
Criar grupos MakeStaticAnalyzerGroup
MakeStaticReloggerGroup
MakeDynamicAnalyzerGroup
MakeDynamicReloggerGroup
A API do C++ fornece funções e tipos auxiliares para agrupar vários objetos de analisador e criação de novo registro. Os grupos são formas organizadas de dividir uma análise complexa em etapas mais simples. O vcperf é organizado dessa forma.
Analisar ou recriar o registro Analisar
Relog
AnalisarA
AnalisarW
RelogA
RelogW

Analisar e recriar o registro

O consumo de um rastreamento é feito por meio de uma sessão de análise ou de uma sessão de recriação do registro do rastreamento.

O uso de uma análise regular é apropriado para a maioria dos cenários. Esse método oferece a flexibilidade para escolher o formato de saída: texto printf, xml, JSON, banco de dados, chamadas REST e assim por diante.

A recriação do registro é útil para análises com finalidades especiais, que precisam produzir um arquivo de saída do ETW. Ao usar a recriação do registro do rastreamento, você consegue converter os eventos do C++ Build Insights em seu próprio formato de evento do ETW. Um uso adequado da recriação de registro seria conectar dados do C++ Build Insights às ferramentas e à infraestrutura existentes do ETW. Por exemplo, o vcperf usa as interfaces de recriação de registro. Isso porque ele deve produzir dados que o Windows Performance Analyzer, uma ferramenta de ETW, possa entender. Alguns conhecimentos prévios de como o ETW funciona são necessários se você planeja usar as interfaces de recriação de registro.

Criação de grupos de analisadores

É importante saber como criar grupos. Veja um exemplo que mostra como criar um grupo de analisadores que imprime Olá, mundo! para cada evento de início de atividade que ele recebe.

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

Usar eventos

Funcionalidade API de C++ API do C Observações
Eventos de correspondência e filtragem MatchEventStackInMemberFunction
MatchEventStack
MatchEventInMemberFunction
MatchEvent
A API do C++ oferece funções que facilitam a extração dos eventos que lhe interessam nos seus rastreamentos. Com a API do C, essa filtragem deve ser feita manualmente.
Tipo de dados de evento Atividade
BackEndPass
De baixo para cima
C1DLL
C2DLL
Geração de código
CommandLine
Compiler
CompilerPass
Variável de ambiente
Evento
EventGroup
Pilha de eventos
ExecutableImageOutput
Saída Exp
FileInput
FileOutput
ForceInlinee
FrontEndFile
FrontEndFileGroup
FrontEndPass
Função
HeaderUnit
ImpLibOutput
Invocação
InvocationGroup
LibOutput
Linker
LinkerGroup
LinkerPass
LTCG
Módulo
ObjOutput
OptICF
OptLBR
OptRef
Pass1
Pass2
PrecompiledHeader
PreLTCGOptRef
SimpleEvent
Nome do Símbolo
TemplateInstantiation
TemplateInstantiationGroup
Thread
De cima para baixo
TraceInfo
TranslationUnitType
Análise do Programa Integral
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

Atividades e eventos simples

Os eventos vêm em duas categorias: atividades e eventos simples. As atividades são processos contínuos no tempo que têm um começo e um fim. Os eventos simples são ocorrências pontuais que não têm uma duração. Ao analisar rastreamentos do MSVC com o SDK do C++ Build Insights, você receberá eventos separados quando uma atividade for iniciada e interrompida. Você receberá apenas um evento quando ocorrer um evento simples.

Relações pai-filho

Atividades e eventos simples estão relacionados entre si por meio de relações pai-filho. O pai de uma atividade ou um evento simples é a atividade abrangente na qual eles ocorrem. Por exemplo, ao compilar um arquivo de origem, o compilador precisa analisar o arquivo e, em seguida, gerar o código. As atividades de análise e geração de código são filhos da atividade do compilador.

Eventos simples não têm uma duração, portanto, nada mais pode acontecer dentro deles. Como tal, eles nunca têm filhos.

As relações pai-filho de cada atividade e evento simples são indicadas na tabela de eventos. Conhecer essas relações é importante ao consumir eventos do C++ Build Insights. Muitas vezes, você precisará se apoiar nessas informações para entender o contexto completo de um evento.

Propriedades

Todos os eventos têm as seguintes propriedades:

Propriedade Descrição
Identificador de tipo Um número que identifica exclusivamente o tipo de evento.
Identificador da instância Um número que identifica exclusivamente o evento no rastreamento. Se dois eventos do mesmo tipo ocorrerem em um rastreamento, ambos obterão um identificador de instância exclusivo.
Hora de início A hora em que uma atividade foi iniciada ou a hora em que ocorreu um evento simples.
Identificador de processo Um número que identifica o processo no qual o evento ocorreu.
Identificador de thread Um número que identifica o thread no qual o evento ocorreu.
Índice do processador Um índice baseado em zero que indica por qual processador lógico o evento foi emitido.
Nome do evento Uma cadeia de caracteres que descreve o tipo do evento.

Todas as atividades que não sejam eventos simples também têm estas propriedades:

Propriedade Descrição
Hora de término A hora em que a atividade parou.
Duração exclusiva O tempo gasto em uma atividade, excluindo o tempo gasto nas respectivas atividades filho.
Tempo de CPU O tempo que a CPU gastou executando código no thread anexado à atividade. Este tempo não inclui a hora em que o thread anexado à atividade estava em suspensão.
Tempo exclusivo da CPU O mesmo que o tempo de CPU, mas excluindo o tempo de CPU gasto pelas atividades filho.
Responsabilidade do tempo do relógio A contribuição da atividade para o tempo geral do relógio. A responsabilidade de tempo do relógio leva em conta o paralelismo entre as atividades. Por exemplo, vamos supor que duas atividades não relacionadas são executadas em paralelo. Ambos têm uma duração de 10 segundos e exatamente o mesmo tempo de início e parada. Nesse caso, o Build Insights atribui uma responsabilidade de tempo de relógio de 5 segundos. Por outro lado, se essas atividades forem executadas uma após a outra sem sobreposição, ambas receberão uma responsabilidade de tempo de relógio de 10 segundos.
Responsabilidade do tempo do relógio exclusiva O mesmo que a responsabilidade de tempo do relógio, mas exclui a responsabilidade de tempo do relógio das atividades filho.

Alguns eventos têm propriedades específicas além das mencionadas. Nesse caso, essas propriedades adicionais são listadas na tabela de eventos.

Consumir eventos fornecidos pelo SDK do C++ Build Insights

A pilha de eventos

Sempre que o SDK do C++ Build Insights lhe entrega um evento, ele vem na forma de uma pilha. A última entrada na pilha é o evento atual e as entradas antes dela são a hierarquia pai. Por exemplo, eventos de início e parada da LTCG ocorrem durante a passagem 1 do vinculador. Nesse caso, a pilha que você recebe conteria: [LINKER, PASS1, LTCG]. A hierarquia pai é conveniente porque você pode rastrear um evento até sua raiz. Se a atividade da LTCG mencionada acima estiver lenta, você poderá saber imediatamente qual invocação do vinculador estava envolvida.

Eventos e pilhas de eventos correspondentes

O SDK do C++ Build Insights lhe fornece todos os eventos em um rastreamento, mas na maioria das vezes você se interessa apenas com um subconjunto deles. Em alguns casos, você pode se interessar apenas por um subconjunto de pilhas de eventos. O SDK fornece meios para ajudá-lo a extrair rapidamente os eventos ou a pilha de eventos de que você precisa e rejeitar as que você não precisa. Isso é feito por meio dessas funções de correspondência:

Função Descrição
MatchEvent Manter um evento se ele corresponder a um dos tipos especificados. Encaminhar eventos correspondentes a um lambda ou a outro tipo que possa ser chamado. A hierarquia pai do evento não é considerada por essa função.
MatchEventInMemberFunction Manter um evento se ele corresponder ao tipo especificado no parâmetro de uma função membro. Encaminhar eventos correspondentes à função membro. A hierarquia pai do evento não é considerada por essa função.
MatchEventStack Manter um evento se o evento e sua hierarquia pai corresponderem aos tipos especificados. Encaminhar o evento e os eventos da hierarquia pai correspondentes para um lambda ou a outro tipo que possa ser chamado.
MatchEventStackInMemberFunction Manter um evento se o evento e sua hierarquia pai corresponderem aos tipos especificados na lista de parâmetros de uma função membro. Encaminhar o evento e os eventos da hierarquia pai correspondentes para a função membro.

As funções correspondentes à pilha de eventos, como MatchEventStack, permitem lacunas ao descrever a hierarquia pai para correspondência. Por exemplo, você pode dizer que está interessado na pilha [LINKER, LTCG]. Isso também corresponderia à pilha [LINKER, PASS1, LTCG]. O último tipo especificado deve ser o tipo de evento correspondente e não fazer parte da hierarquia pai.

Classes de captura

O uso das funções Match* requer que você especifique os tipos que deseja obter correspondência. Esses tipos são selecionados em uma lista de classes de captura. As classes de captura vêm em várias categorias descritas abaixo.

Categoria Descrição
Exato Essas classes de captura são usadas para correspondência com um tipo de evento específico e nenhum outro. Um exemplo é a classe Compiler, que corresponde ao evento COMPILER.
Curinga Essas classes de captura podem ser usadas para correspondência com qualquer evento da lista de eventos a que dão suporte. Por exemplo, o curinga Activity corresponde a qualquer evento de atividade. Outro exemplo é o curinga CompilerPass, que pode corresponder ao evento FRONT_END_PASS ou BACK_END_PASS.
Grupo Os nomes das classes de captura de grupo terminam em Group. Elas são usadas para correspondência com vários eventos do mesmo tipo em uma linha, ignorando lacunas. As classes de captura de grupo só fazem sentido ao combinar eventos recursivos, porque você não sabe quantos existem na pilha de eventos. Por exemplo, a atividade FRONT_END_FILE acontece sempre que o compilador analisa um arquivo. Essa atividade é recursiva porque o compilador pode encontrar uma diretiva de inclusão ao analisar o arquivo. A classe FrontEndFile corresponde a apenas um evento FRONT_END_FILE na pilha. Use a classe FrontEndFileGroup para fazer correspondência com toda a hierarquia de inclusão.
Grupo curinga Um grupo curinga combina as propriedades de curingas e grupos. A única classe dessa categoria é InvocationGroup, que obtém a correspondência e captura todos os eventos LINKER e COMPILER em uma única pilha de eventos.

Consulte a tabela de eventos para saber quais classes de captura podem ser usadas para correspondência com cada evento.

Após a correspondência: como usar eventos capturados

Depois que a correspondência for concluída com êxito, as funções Match* construirão os objetos da classe de captura e os encaminharão para a função especificada. Use esses objetos de classe de captura para acessar as propriedades dos eventos.

Exemplo

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