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:
- En Visual Studio 2017 y versiones posteriores, cree un proyecto nuevo de C++.
- En el panel del Explorador de soluciones, haga clic con el botón derecho en su proyecto.
- Seleccione Administrar paquetes NuGet en el menú contextual.
- En la parte superior derecha, seleccione el origen del paquete nuget.org.
- Busque la versión más reciente del paquete Microsoft.Cpp.BuildInsights.
- Elija Instalar.
- 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 un seguimiento
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 un seguimiento se puede hacer de dos maneras:
Método 1: uso de vcperf en Visual Studio 2019 y versiones posteriores
Abra un símbolo del sistema de las herramientas nativas x64 con privilegios elevados para VS 2019.
Ejecute el siguiente comando:
vcperf /start MySessionName
Compile el proyecto.
Ejecute el siguiente comando:
vcperf /stopnoanalyze MySessionName outputTraceFile.etl
Importante
Use el comando
/stopnoanalyze
al detener el seguimiento con vcperf. No puede usar el SDK de C++ Build Insights para analizar los seguimientos detenidos por el comando/stop
habitual.
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 inicio y detención de seguimiento requieren privilegios administrativos. Todas las demás funciones del SDK de C++ Build Insights se pueden ejecutar sin ellos.
Funciones del SDK relacionadas con la recopilación de seguimiento
Funcionalidad | API de C++ | API de C |
---|---|---|
Inicio de un seguimiento | StartTracingSession | StartTracingSessionA StartTracingSessionW |
Detención de un seguimiento | StopTracingSession | StopTracingSessionA StopTracingSessionW |
Detención de un seguimiento y análisis inmediato del resultado |
StopAndAnalyzeTracingSession | StopAndAnalyzeTracingSessionA StopAndAnalyzeTracingSession |
Detención de un seguimiento y registro inmediato 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 un seguimiento
Una vez que tenga un seguimiento 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 se recomienda consumir el seguimiento 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 establece, mientras que el formato de seguimiento de ETW sin procesar está sujeto a cambios sin previo aviso.
Funciones y tipos del SDK relacionados con el consumo de seguimiento
Funcionalidad | API de C++ | API de C | Notas |
---|---|---|---|
Configuración de devoluciones de llamada de evento | 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 de devolución de llamada mediante la creación de una clase de analizador o de registrador que herede la interfaz IAnalyzer o IRelogger. En C, implemente las devoluciones de llamada en funciones globales y proporcione punteros que las señalen 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 registro
El consumo de un seguimiento se realiza a través de una sesión de análisis o una sesión de 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 registro es para los análisis de uso especial que necesitan generar un archivo de salida de ETW. Mediante el registro, 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 usa las interfaces de registro. Esto es porque debe generar datos que Windows Performance Analyzer, una herramienta de ETW, pueda entender. Si planea usar las interfaces de registro, se requiere ciertos conocimientos previos de cómo funciona 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
Funciones y tipos del SDK relacionados con los eventos
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 seguimientos de MSVC con el SDK de C++ Build Insights, recibirá eventos independientes cuando una actividad se inicie y se detenga. Recibirá solo un evento cuando se produzca un evento sencillo.
Relaciones de elementos primarios y secundarios
Las actividades y los eventos sencillos se relacionan entre sí a través de las relaciones de elementos primarios y secundarios. El elemento primario de una actividad o de un evento sencillo es la actividad englobante en la que se producen. 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. De ese modo, nunca tienen elementos secundarios.
Las relaciones de elementos primarios y secundarios 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 | Número que identifica el evento dentro del seguimiento de manera única. 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 |
---|---|
Hora de detención | 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 en suspensión. |
Tiempo de CPU exclusivo | Igual que el tiempo de CPU, pero sin incluir el tiempo que la CPU dedica a sus 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 tiempo de reloj de 5 segundos. 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 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
Cada vez que el SDK de C++ Build Insights proporciona un evento, viene en formato de 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 SDK de C++ Build Insights le proporciona todos los eventos de un seguimiento, pero la mayor parte del tiempo solo le 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 o a otro tipo al que se puede 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 primaria coincidente a una expresión lambda o a otro tipo al que se puede llamar. |
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, digamos 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 hacer coincidir. 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.
Category | Descripción |
---|---|
Exact | Estas clases de captura se usan para coincidir con un tipo de evento específico 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 coincidir con varios eventos del mismo tipo en una fila, sin considerar las lagunas. 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 de caracteres 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ía 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;
}
}