Partilhar via


C++ Build Insights SDK

O C++ Build Insights SDK é compatível com o Visual Studio 2017 e posterior. Para ver a documentação destas versões, defina o controlo seletor de versão do Visual Studio para este artigo para Visual Studio 2017 ou posterior. Encontra-se na parte superior do índice desta página.

O C++ Build Insights SDK é uma coleção de APIs que permitem criar ferramentas personalizadas sobre a plataforma C++ Build Insights. Esta página fornece uma visão geral de alto nível para ajudá-lo a começar.

Obtendo o SDK

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

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

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

Recolha de um vestígio

Usar o C++ Build Insights SDK para analisar eventos que saem da cadeia de ferramentas MSVC requer que você primeiro colete um rastreamento. O SDK usa o Rastreamento de Eventos para Windows (ETW) como a tecnologia de rastreamento subjacente. A recolha de um vestígio pode ser feita de duas formas:

Método 1: usando vcperf no Visual Studio 2019 e superior

  1. Abra um prompt de comando de ferramentas nativas x64 elevado para o VS 2019.

  2. Execute o seguinte comando: vcperf /start MySessionName

  3. Construa o seu projeto.

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

    Importante

    Utilize o comando /stopnoanalyze ao parar o seu rastreamento com vcperf. Não é possível usar o C++ Build Insights SDK para analisar rastreamentos interrompidos pelo comando regular /stop .

Método 2: programaticamente

Use qualquer uma dessas funções de coleta de rastreamento do C++ Build Insights SDK 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 rastreamento de início e fim exigem privilégios administrativos. Todas as outras funções no C++ Build Insights SDK podem ser executadas sem elas.

Funcionalidade C++ API C API
Iniciando um rastreamento StartTracingSession StartTracingSessionA
StartTracingSessionW
Parar um rastreio StopTracingSession StopTracingSessionA
StopTracingSessionW
Parar um rastreio e
analisar imediatamente o resultado
StopAndAnalyzeTracingSession StopAndAnalyzeTracingSessionA
StopAndAnalyzeTracingSession
Parar um rastreio e
reregistrando imediatamente o resultado
StopAndRelogTracingSession StopAndRelogTracingSessionA
StopAndRelogTracingSessionW

As secções seguintes mostram como configurar uma análise ou uma sessão de relogging. É necessário para as funções de funcionalidade combinadas, como StopAndAnalyzeTracingSession.

Consumir um vestígio

Depois de ter um rastreamento ETW, use o C++ Build Insights SDK para descompactá-lo. O SDK oferece os eventos em um formato que permite que você desenvolva suas ferramentas rapidamente. Não recomendamos consumir o traço ETW bruto sem usar o SDK. O formato de evento usado pelo MSVC não é documentado, otimizado para ser dimensionado para grandes compilações e difícil de entender. Além disso, a API do SDK do C++ Build Insights é estável, enquanto o formato de rastreamento ETW bruto está sujeito a alterações sem aviso prévio.

Funcionalidade C++ API C API Observações
Configurando callbacks de eventos IAnalyzer
IRelogger
ANALYSIS_CALLBACKS
RELOG_CALLBACKS
O C++ Build Insights SDK fornece eventos através de funções de callback. Em C++, implemente as funções de callback criando uma classe de analisador ou relogger que herda da interface IAnalyzer ou IRelogger. Em C, implemente os retornos de chamada em funções globais e forneça ponteiros para eles na estrutura ANALYSIS_CALLBACKS ou RELOG_CALLBACKS.
Grupos de construção MakeStaticAnalyzerGroup
MakeStaticReloggerGroup
MakeDynamicAnalyzerGroup
MakeDynamicReloggerGroup
A API C++ fornece funções e tipos auxiliares para agrupar vários objetos do analisador e do relogger. Os grupos são uma boa maneira de dividir uma análise complexa em etapas mais simples. O VCPERF está organizado desta forma.
Analisando ou reregistrando Analisar
Relog
AnalisaA
AnalisarW
RelogA
RelogW

Analisando e efetuando novo registo

O consumo de um rastreamento é feito por meio de uma sessão de análise ou de uma sessão de reregistro.

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

Relogging é para análises de fins especiais que precisam produzir um arquivo de saída ETW. Usando o relogging, você pode traduzir os eventos do C++ Build Insights em seu próprio formato de evento ETW. Um uso apropriado do relogging seria conectar os dados do C++ Build Insights às suas ferramentas e infraestrutura ETW existentes. Por exemplo, vcperf faz uso das interfaces de relogging. Isso porque ele deve produzir dados que o Windows Performance Analyzer, uma ferramenta ETW, possa entender. É necessário algum conhecimento prévio de como o ETW funciona caso pretenda usar as interfaces de regravação.

Criação de grupos de analisadores

É importante saber como criar grupos. Aqui está um exemplo que mostra como criar um grupo de analisadores que imprime Olá, mundo! para cada evento de início de atividade que 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;
}

Usando eventos

Funcionalidade C++ API C API Observações
Combinação e filtragem de eventos MatchEventStackInMemberFunction
MatchEventStack
MatchEventInMemberFunction
MatchEvent
A API C++ oferece funções que facilitam a extração dos eventos importantes de seus rastreamentos. Com a API C, essa filtragem deve ser feita manualmente.
Tipos de dados de eventos Atividade
BackEndPass
De baixo para cima
C1DLL
C2DLL
Geração de código
Linha de comando
Compilador
CompilerPass
EnvironmentVariable
Evento
Grupo de Eventos
Pilha de eventos
ExecutableImageOutput
ExpOutput
FileInput
FileOutput
ForceInlinee
FrontEndFile
FrontEndFileGroup
FrontEndPass
Função
Unidade de cabeçalho
ImpLibOutput
Invocação
Grupo de Invocação
LibOutput
Vinculador
LinkerGroup
LinkerPass
LTCG
Módulo
ObjOutput
OptICF
OptLBR
OptRef
Passe1
Passe2
PrecompiledHeader
PreLTCGOptRef
SimpleEvent
SymbolName
TemplateInstantiation
TemplateInstantiationGroup
Tópico
De cima para baixo
TraceInfo
TipoUnidadeTradução
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
TIPO_DE_UNIDADE_DE_TRADUÇÃO_DADOS

Atividades e eventos simples

Os eventos dividem-se 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. Eventos simples são ocorrências pontuais e não têm duração. Ao analisar rastreamentos MSVC com o C++ Build Insights SDK, você receberá eventos separados quando uma atividade for iniciada e interrompida. Você receberá apenas um evento quando ocorrer um evento simples.

Relações entre pais e filhos

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

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

As relações pai-filho de cada atividade e evento simples são indicadas na tabela de eventos. Conhecer esses relacionamentos é importante ao consumir eventos do C++ Build Insights. Muitas vezes, você terá que confiar neles 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 de instância Um número que identifica exclusivamente o evento dentro do rasto. 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 começou ou a hora em que um evento simples ocorreu.
Identificador do processo Um número que identifica o processo em que 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 de evento.

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

Propriedade Descrição
Parar o tempo A hora em que a atividade parou.
Duração exclusiva O tempo gasto em uma atividade, excluindo o tempo gasto em suas atividades infantis.
Tempo de CPU O tempo que a CPU gastou executando código no thread anexado à atividade. Não inclui o tempo em que o fio ligado à atividade estava dormindo.
Tempo exclusivo de CPU O mesmo que o tempo da CPU, mas excluindo o tempo da CPU gasto pelas atividades da criança.
Responsabilidade pelo tempo do relógio de parede A contribuição da atividade para o tempo total do relógio de parede. A responsabilidade pelo tempo de relógio de parede leva em conta o paralelismo entre as atividades. Por exemplo, vamos supor que duas atividades não relacionadas sejam executadas em paralelo. Ambos têm uma duração de 10 segundos e exatamente o mesmo tempo de início e parada. Neste caso, o Build Insights atribui a ambos uma responsabilidade de tempo de relógio de parede 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 parede de 10 segundos.
Responsabilidade exclusiva pelo tempo do relógio de parede O mesmo que a responsabilidade do tempo do relógio de parede, mas exclui a responsabilidade do tempo do relógio de parede das atividades infantis.

Alguns eventos têm propriedades próprias além das mencionadas. Nesse caso, essas propriedades adicionais são listadas na tabela de eventos.

Consumindo eventos fornecidos pelo C++ Build Insights SDK

A pilha de eventos

Sempre que o C++ Build Insights SDK fornece 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 sua hierarquia de pai. Por exemplo, os eventos de início e parada do LTCG ocorrem durante a passagem 1 do vinculador. Neste caso, a pilha que você receberia contém: [LINKER, PASS1, LTCG]. A hierarquia pai é conveniente porque você pode rastrear um evento até sua raiz. Se a atividade LTCG mencionada acima for lenta, você poderá saber imediatamente qual invocação de vinculador estava envolvida.

Eventos correspondentes e pilhas de eventos

O C++ Build Insights SDK oferece todos os eventos em um rastreamento, mas na maioria das vezes você só se preocupa com um subconjunto deles. Em alguns casos, você pode se preocupar apenas com um subconjunto de pilhas de eventos. O SDK fornece recursos para ajudá-lo a extrair rapidamente os eventos ou a pilha de eventos de que você precisa e rejeitar os que você não precisa. É feito através destas funções correspondentes:

Função Descrição
MatchEvent Mantenha um evento se ele corresponder a um dos tipos especificados. Encaminhe eventos correspondentes para um lambda ou outro tipo chamável. A hierarquia pai do evento não é considerada por essa função.
MatchEventInMemberFunction Mantenha um evento se ele corresponder ao tipo especificado no parâmetro de uma função membro. Encaminhe os eventos correspondentes para a função de membro. A hierarquia pai do evento não é considerada por essa função.
MatchEventStack Mantenha um evento caso tanto o evento quanto a sua hierarquia pai correspondam aos tipos especificados. Encaminhe o evento e os eventos de hierarquia pai correspondentes para um lambda ou outro tipo chamável.
MatchEventStackInMemberFunction Mantenha um evento se o evento e a sua hierarquia parental correspondam aos tipos especificados na lista de parâmetros de uma função membro. Encaminhe o evento e os eventos correspondentes da hierarquia pai para a função de membro.

As funções de correspondência da pilha de eventos, como MatchEventStack, permitem lacunas quando se descreve a hierarquia pai a ser correspondida. Por exemplo, tu podes dizer que estás interessado na stack [LINKER,LTCG]. Isto também corresponderia à pilha [LINKER, PASS1, LTCG]. O último tipo especificado deve ser o tipo de evento a ser comparado e não faz parte da hierarquia superior.

Classes de captura

O uso das funções Match* requer que especifiques os tipos que desejas que correspondam. Esses tipos são selecionados a partir de 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 corresponder a um tipo de evento específico e nenhum outro. Um exemplo é a classe Compiler , que corresponde ao evento COMPILER .
Caráter curinga Essas classes de captura podem ser usadas para corresponder a qualquer evento da lista de eventos suportados. Por exemplo, o wildcard Atividade corresponde a qualquer evento de atividade. Outro exemplo é o wildcard CompilerPass, que pode corresponder ao evento FRONT_END_PASS ou BACK_END_PASS.
Grupo Os nomes das classes de captura de grupo terminam em Grupo. São usados para combinar vários eventos do mesmo tipo consecutivamente, ignorando intervalos. Eles 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 toda vez que o compilador analisa um arquivo. Essa atividade é recursiva porque o compilador pode encontrar uma diretiva include enquanto está analisando o arquivo. A classe FrontEndFile corresponde a apenas um evento FRONT_END_FILE na pilha. Use a classe FrontEndFileGroup para corresponder a toda a hierarquia de inclusão.
Grupo comodín Um grupo curinga combina as propriedades de curinga e de grupo. A única classe dessa categoria é InvocationGroup, que corresponde 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 corresponder a cada evento.

Após a correspondência, utilização de eventos capturados

Quando um jogo é concluído com êxito, as Match* funções constroem os objectos de captura e enviam-nos 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;
    }
}