SDK de C++ Build Insights

El SDK de C++ Build Insights es compatible con Visual Studio 2017 y versiones posteriores. Para ver la documentación de estas versiones, establezca el control de selector de Versión de Visual Studio para este artículo en Visual Studio 2017 o versiones posteriores. Se encuentra en la parte superior de la tabla de contenido de esta página.

El SDK de C++ Build Insights es una colección de API que permiten crear herramientas personalizadas basadas en la plataforma de C++ Build Insights. En esta página se proporciona información general de alto nivel que lo ayudará a empezar.

Obtención del SDK

Siga estos pasos para descargar el SDK de C++ Build Insights como un paquete NuGet:

  1. En Visual Studio 2017 y versiones posteriores, cree un proyecto nuevo de C++.
  2. En el panel del Explorador de soluciones, haga clic con el botón derecho en su proyecto.
  3. Seleccione Administrar paquetes NuGet en el menú contextual.
  4. En la parte superior derecha, seleccione el origen del paquete nuget.org.
  5. Busque la versión más reciente del paquete Microsoft.Cpp.BuildInsights.
  6. Elija Instalar.
  7. Acepte la licencia.

Siga leyendo para obtener información sobre los conceptos generales relacionados con el SDK. También puede acceder al repositorio GitHub oficial de ejemplos de C++ Build Insights para ver ejemplos de aplicaciones de C++ reales que usan el SDK.

Recopilación de una traza

El uso del SDK de C++ Build Insights para analizar eventos que proceden de la cadena de herramientas de MSVC requiere que primero se recopile un seguimiento. El SDK usa el Seguimiento de eventos para Windows (ETW) como la tecnología de seguimiento subyacente. La recopilación de una traza se puede hacer de dos maneras:

Método 1: uso de vcperf en Visual Studio 2019 y versiones posteriores

  1. Abra una ventana de comandos de las herramientas de desarrollo x64 con derechos administrativos para VS 2019.

  2. Ejecute el siguiente comando: vcperf /start MySessionName

  3. Construye tu proyecto.

  4. Ejecute el siguiente comando: vcperf /stopnoanalyze MySessionName outputTraceFile.etl

    Importante

    Use el comando /stopnoanalyze al detener el seguimiento con vcperf. No se puede usar el SDK de C++ Build Insights para analizar las trazas detenidas por el comando /stop normal.

Método 2: mediante programación

Use cualquiera de estas funciones de recopilación de seguimiento del SDK de C++ Build Insights para iniciar y detener los seguimientos mediante programación. El programa que ejecuta estas llamadas de función debe tener privilegios administrativos. Solo las funciones de iniciar y detener el seguimiento requieren privilegios administrativos. Todas las demás funciones del SDK de C++ Build Insights se pueden ejecutar sin ellos.

Funcionalidad API de C++ API de C
Inicio de un seguimiento StartTracingSession StartTracingSessionA
StartTracingSessionW
Parar una traza StopTracingSession StopTracingSessionA
StopTracingSessionW
Detención de un seguimiento y
análisis inmediato del resultado
StopAndAnalyzeTracingSession StopAndAnalyzeTracingSessionA
StopAndAnalyzeTracingSession
Detención de una traza y
registro inmediato nuevamente del resultado
StopAndRelogTracingSession StopAndRelogTracingSessionA
StopAndRelogTracingSessionW

En las secciones siguientes se muestra cómo configurar una sesión de análisis o de registro. Es necesario para las funciones de funcionalidad combinada, como StopAndAnalyzeTracingSession.

Consumo de una traza

Una vez que tenga un rastro de ETW, use el SDK de C++ Build Insights para desempaquetarlo. El SDK proporciona los eventos en un formato que le permite desarrollar rápidamente sus herramientas. No recomendamos consumir la traza de ETW sin procesar sin usar el SDK. El formato de evento que el MSVC usa no está documentado, está optimizado para escalar a compilaciones muy grandes y es difícil de entender. Además, la API del SDK de C++ Build Insights es estable, mientras que el formato de traza ETW sin procesar está sujeto a cambios sin previo aviso.

Funcionalidad API de C++ API de C Notas
Configuración de callbacks de eventos IAnalyzer
IRelogger
ANALYSIS_CALLBACKS
RELOG_CALLBACKS
El SDK de C++ Build Insights proporciona eventos a través de funciones de devolución de llamada. En C++, implemente las funciones callback creando una clase analizador o registrador que herede la interfaz IAnalyzer o IRelogger. En C, implemente los callbacks en funciones globales y proporcione punteros hacia ellos en la estructura ANALYSIS_CALLBACKS o RELOG_CALLBACKS.
Creación de grupos MakeStaticAnalyzerGroup
MakeStaticReloggerGroup
MakeDynamicAnalyzerGroup
MakeDynamicReloggerGroup
La API de C++ proporciona tipos y funciones auxiliares para agrupar en conjunto varios objetos de analizador y de registro. Los grupos son una manera práctica de dividir un análisis complejo en pasos más sencillos. vcperf está organizado de esta manera.
Análisis o registro Analizar
Registro
AnalyzeA
AnalyzeW
RelogA
RelogW

Análisis y re-registro

El consumo de una traza se realiza a través de una sesión de análisis o una sesión de re-registro.

El uso de un análisis normal es adecuado para la mayoría de los escenarios. Este método ofrece la flexibilidad de elegir el formato de salida: printf texto, XML, JSON, base de datos, llamadas REST, etc.

El re-registro es para análisis de propósito especial que necesitan generar un archivo de salida ETW. Mediante 'relogging', puede traducir los eventos de C++ Build Insights a su propio formato de evento ETW. Un uso apropiado del registro sería enlazar datos de C++ Build Insights a la infraestructura y las herramientas de ETW existentes. Por ejemplo, vcperf hace uso de las interfaces de reprocesamiento de registros. Esto es porque debe generar datos que Windows Performance Analyzer, una herramienta de ETW, pueda entender. Si planea usar las interfaces de re-registro, se requieren ciertos conocimientos previos sobre el funcionamiento de ETW.

Creación de grupos de analizadores

Resulta importante saber cómo crear grupos. Este es un ejemplo que muestra cómo crear un grupo de analizadores que imprime el mensaje Hola mundo para cada inicio de actividad que se reciba.

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 de eventos

Funcionalidad API de C++ API de C Notas
Emparejamiento y filtrado de eventos MatchEventStackInMemberFunction
MatchEventStack
MatchEventInMemberFunction
MatchEvent
La API de C++ ofrece funciones que facilitan la extracción de los eventos que le interesan de los seguimientos. Con la API de C, este filtrado se debe hacer manualmente.
Tipos de datos de eventos Actividad
BackEndPass
De abajo hacia arriba
C1DLL
C2DLL
CodeGeneration
CommandLine
Compilador
CompilerPass
EnvironmentVariable
Evento
EventGroup
EventStack
ExecutableImageOutput
ExpOutput
FileInput
FileOutput
ForceInlinee
FrontEndFile
FrontEndFileGroup
FrontEndPass
Función
HeaderUnit
ImpLibOutput
Invocación
InvocationGroup
LibOutput
Enlazador
LinkerGroup
LinkerPass
LTCG
Módulo
ObjOutput
OptICF
OptLBR
OptRef
Pass1
Pass2
PrecompiledHeader
PreLTCGOptRef
SimpleEvent
SymbolName
TemplateInstantiation
TemplateInstantiationGroup
Subproceso
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

Actividades y eventos sencillos

Los eventos tienen dos categorías: actividades y eventos sencillos. Las actividades son procesos continuos en el tiempo que tienen un inicio y un final. Los eventos sencillos son ocurrencias puntuales sin duración. Al analizar los rastros de MSVC con el SDK de C++ Build Insights, recibirá eventos independientes cuando una actividad inicia y se detiene. Recibirá solo un evento cuando ocurra un evento simple.

Relaciones entre padre e hijo

Las actividades y los eventos sencillos se relacionan entre sí mediante relaciones padre-hijo. El padre de una actividad o de un evento sencillo es la actividad matriz en la cual ocurren. Por ejemplo, al compilar un archivo de código fuente, el compilador tiene que analizar el archivo y, luego, generar el código. Las actividades de análisis y generación de código son elementos secundarios de la actividad del compilador.

Los eventos sencillos no tienen una duración, por lo que no puede ocurrir nada más en ellos. Por lo tanto, nunca tienen hijos.

Las relaciones entre padres e hijos de cada actividad y evento sencillo se indican en la tabla de eventos. Conocer estas relaciones resulta importante al consumir eventos de C++ Build Insights. Con frecuencia tendrá que basarse en ellos para entender el contexto completo de un evento.

Propiedades

Todos los eventos tienen las propiedades siguientes:

Propiedad Descripción
Identificador de tipo Número que identifica el tipo de evento de manera única.
Identificador de instancia Un número que identifica de manera única el evento dentro de la traza. Si en un seguimiento se producen dos eventos del mismo tipo, ambos obtienen un identificador de instancia único.
Hora de inicio Hora a la que se inició una actividad o la hora a la que se produjo un evento sencillo.
Identificador de proceso Número que identifica el proceso en el que se produjo el evento.
Identificador de subproceso Número que identifica el subproceso en el que se produjo el evento.
Índice de procesador Índice de base cero que indica qué procesador lógico emitió el evento.
Nombre del evento Cadena que describe el tipo de evento.

Todas las actividades distintas de los eventos sencillos también tienen estas propiedades:

Propiedad Descripción
Tiempo de parada Hora a la que se detuvo la actividad.
Duración exclusiva Tiempo dedicado a una actividad, sin incluir el tiempo dedicado a sus actividades secundarias.
Tiempo de CPU Tiempo que la CPU dedica a ejecutar código en el subproceso adjunto a la actividad. No incluye el tiempo durante el cual el subproceso adjunto a la actividad estaba dormido.
Tiempo de CPU exclusivo Igual que el tiempo de CPU, pero excluyendo el tiempo de CPU dedicado a las actividades secundarias.
Responsabilidad de tiempo de reloj Contribución de la actividad al tiempo de reloj global. La responsabilidad de tiempo de reloj tiene en cuenta el paralelismo entre las actividades. Por ejemplo, supongamos que dos actividades no relacionadas se ejecutan en paralelo. Ambos tienen una duración de 10 segundos y exactamente la misma hora de inicio y de finalización. En este caso, Build Insights asigna a ambos una responsabilidad de 5 segundos de tiempo en reloj. Por el contrario, si estas actividades se ejecutan una tras otra sin superposición, se les asigna a ambas una responsabilidad de tiempo de reloj de pared de 10 segundos.
Responsabilidad de tiempo de reloj exclusiva Igual que la responsabilidad de tiempo de reloj, pero excluye la responsabilidad de tiempo de reloj de las actividades secundarias.

Algunos eventos tienen sus propias propiedades más allá de las mencionadas. En este caso, estas propiedades adicionales se indican en la tabla de eventos.

Consumo de eventos proporcionados por el SDK de C++ Build Insights

La pila de eventos

Cuando el SDK de C++ Build Insights te proporciona un evento, viene en forma de una pila. La última entrada de la pila es el evento actual y las entradas anteriores son su jerarquía primaria. Por ejemplo, los eventos de inicio y detención de LTCG ocurren durante el paso 1 del enlazador. En este caso, la pila que se recibiría contiene: [LINKER, PASS1, LTCG]. La jerarquía primaria resulta útil porque es posible hacer el seguimiento de un evento hasta su raíz. Si la actividad de LTCG mencionada anteriormente es lenta, puede saber de inmediato qué invocación del enlazador estuvo implicada.

Eventos y pilas de eventos coincidentes

El C++ Build Insights SDK te proporciona todos los eventos de un seguimiento, pero la mayor parte del tiempo, solo te interesa un subconjunto de ellos. En algunos casos, es posible que solo le interese un subconjunto de pilas de eventos. El SDK proporciona los recursos para ayudarlo a extraer rápidamente los eventos o la pila de eventos que necesita y a rechazar los que no. Esto se hace a través de estas funciones coincidentes:

Función Descripción
MatchEvent Conserve un evento si coincide con uno de los tipos especificados. Reenvíe los eventos coincidentes a una expresión lambda u otro tipo de función que se pueda llamar. Esta función no toma en cuenta la jerarquía primaria del evento.
MatchEventInMemberFunction Conserve un evento si coincide con el tipo especificado en el parámetro de una función miembro. Reenvíe los eventos coincidentes a la función miembro. Esta función no toma en cuenta la jerarquía primaria del evento.
MatchEventStack Conserve un evento si tanto el evento como su jerarquía primaria coinciden con los tipos especificados. Reenvíe el evento y los eventos de la jerarquía principal coincidente a una expresión lambda u otro tipo invocable.
MatchEventStackInMemberFunction Conserve un evento si tanto el evento como su jerarquía primaria coinciden con los tipos especificados en la lista de parámetros de una función miembro. Reenvié el evento y los eventos de la jerarquía primaria coincidente a la función miembro.

Las funciones coincidentes de la pila de eventos como MatchEventStack permiten hacer coincidir las lagunas en la descripción de la jerarquía primaria. Por ejemplo, puede decir que le interesa la pila [LINKER, LTCG]. También coincidiría con la pila [LINKER, PASS1, LTCG]. El último tipo especificado debe ser el tipo de evento con que se va a coincidir y no forma parte de la jerarquía primaria.

Clases de captura

El uso de las funciones Match* requiere especificar los tipos que quiere emparejar. Estos tipos se seleccionan de una lista de clases de captura. Las clases de captura tienen varias categorías, las que se describen a continuación.

Categoría Descripción
Exacto Estas clases de captura se usan para corresponder a un tipo específico de evento y ningún otro. Un ejemplo es la clase Compiler, que coincide con el evento COMPILER.
Wildcard (Carácter comodín) Estas clases de captura se pueden usar para coincidir con cualquier evento de la lista de eventos que admiten. Por ejemplo, el carácter comodín Activity coincide con cualquier evento de actividad. Otro ejemplo es el carácter comodín CompilerPass, que puede coincidir con el evento FRONT_END_PASS o con el evento BACK_END_PASS.
Grupo Los nombres de las clases de captura de grupo finalizan en Group. Se usan para emparejar varios eventos del mismo tipo consecutivamente, ignorando huecos. Solo tienen sentido al hacer coincidir eventos recursivos, porque no se sabe cuántos hay en la pila de eventos. Por ejemplo, la actividad FRONT_END_FILE se produce cada vez que el compilador analiza un archivo. Esta actividad es recursiva porque el compilador podría encontrar una directiva Include al analizar el archivo. La clase FrontEndFile solo coincide con un evento FRONT_END_FILE de la pila. Use la clase FrontEndFileGroup para coincidir con toda la jerarquía Include.
Grupo comodín Un grupo de caracteres comodín combina las propiedades de los caracteres comodín y los grupos. La única clase de esta categoría es InvocationGroup, que coincide con todos los eventos LINKER y COMPILER de una sola pila de eventos y los captura.

Consulte la tabla de eventos para saber qué clases de captura se pueden usar para coincidir con cada evento.

Después de la coincidencia: uso de los eventos capturados

Una vez que se completa correctamente una coincidencia, las funciones Match* construyen los objetos de la clase de captura y los reenvían a la función especificada. Use estos objetos de la clase de captura para acceder a las propiedades de los eventos.

Ejemplo

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