Udostępnij za pośrednictwem


Zestaw SDK ze szczegółowymi informacjami o kompilowaniu w języku C++

Zestaw SDK usługi Build Insights języka C++ jest zgodny z programem Visual Studio 2017 lub nowszym. Aby zapoznać się z dokumentacją dla tych wersji, ustaw kontrolkę selektora wersji programu Visual Studio dla tego artykułu na program Visual Studio 2017 lub nowszy. Znajduje się on w górnej części spisu treści na tej stronie.

Zestaw SDK usługi Build Insights języka C++ to kolekcja interfejsów API, które umożliwiają tworzenie spersonalizowanych narzędzi na platformie C++ Build Insights. Ta strona zawiera ogólne omówienie ułatwiające rozpoczęcie pracy.

Uzyskiwanie zestawu SDK

Zestaw SDK usługi Build Insights języka C++ można pobrać jako pakiet NuGet, wykonując następujące kroki:

  1. W programie Visual Studio 2017 lub nowszym utwórz nowy projekt C++.
  2. W okienku Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt.
  3. Wybierz pozycję Zarządzaj pakietami NuGet z menu kontekstowego.
  4. W prawym górnym rogu wybierz źródło pakietu nuget.org .
  5. Wyszukaj najnowszą wersję pakietu Microsoft.Cpp.BuildInsights.
  6. Wybierz pozycję Zainstaluj.
  7. Zaakceptuj licencję.

Przeczytaj, aby uzyskać informacje na temat ogólnych pojęć związanych z zestawem SDK. Możesz również uzyskać dostęp do oficjalnego repozytorium GitHub przykładów szczegółowych informacji kompilacji języka C++, aby zobaczyć przykłady rzeczywistych aplikacji języka C++ korzystających z zestawu SDK.

Zbieranie śladu

Korzystanie z zestawu SDK usługi Build Insights języka C++ do analizowania zdarzeń wychodzących z łańcucha narzędzi MSVC wymaga, aby najpierw zebrać ślad. Zestaw SDK używa funkcji śledzenia zdarzeń dla systemu Windows (ETW) jako podstawowej technologii śledzenia. Zbieranie śladu można wykonać na dwa sposoby:

Metoda 1: używanie narzędzia vcperf w programie Visual Studio 2019 lub nowszym

  1. Otwórz wiersz polecenia narzędzi natywnych x64 z podwyższonym poziomem uprawnień dla programu VS 2019.

  2. Uruchom następujące polecenie: vcperf /start MySessionName

  3. Skompiluj projekt.

  4. Uruchom następujące polecenie: vcperf /stopnoanalyze MySessionName outputTraceFile.etl

    Ważne

    Użyj polecenia podczas zatrzymywania /stopnoanalyze śledzenia za pomocą narzędzia vcperf. Nie można użyć zestawu SDK usługi Build Insights języka C++ do analizowania śladów zatrzymanych przez zwykłe /stop polecenie.

Metoda 2. Programowo

Użyj dowolnej z tych funkcji zbierania śladów zestawu SDK usługi Build Insights w języku C++, aby programowo uruchamiać i zatrzymywać ślady. Program wykonujący te wywołania funkcji musi mieć uprawnienia administracyjne. Tylko funkcje uruchamiania i zatrzymywania śledzenia wymagają uprawnień administracyjnych. Wszystkie inne funkcje zestawu SDK usługi Build Insights języka C++ można wykonać bez nich.

Funkcje Interfejs API języka C++ C API
Uruchamianie śledzenia StartTracingSession StartTracingSessionA
StartTracingSessionW
Zatrzymywanie śledzenia StopTracingSession StopTracingSessionA
StopTracingSessionW
Zatrzymywanie śledzenia i
natychmiastowe analizowanie wyniku
StopAndAnalyzeTracingSession StopAndAnalyzeTracingSessionA
StopAndAnalyzeTracingSession
Zatrzymywanie śledzenia i
natychmiastowe ponowne rejestrowanie wyniku
StopAndRelogTracingSession StopAndRelogTracingSessionA
StopAndRelogTracingSessionW

W poniższych sekcjach pokazano, jak skonfigurować analizę lub sesję rejestrowania. Jest to wymagane dla połączonych funkcji, takich jak StopAndAnalyzeTracingSession.

Korzystanie ze śledzenia

Gdy masz ślad ETW, użyj zestawu SDK usługi Build Insights języka C++, aby go rozpakować. Zestaw SDK udostępnia zdarzenia w formacie umożliwiającym szybkie opracowywanie narzędzi. Nie zalecamy używania nieprzetworzonego śledzenia ETW bez korzystania z zestawu SDK. Format zdarzenia używany przez MSVC jest nieudokumentowany, zoptymalizowany pod kątem skalowania do ogromnych kompilacji i trudno zrozumieć. Ponadto interfejs API zestawu SDK usługi Build Insights języka C++ jest stabilny, podczas gdy nieprzetworzonego formatu śledzenia ETW może ulec zmianie bez powiadomienia.

Funkcje Interfejs API języka C++ C API Uwagi
Konfigurowanie wywołań zwrotnych zdarzeń IAnalyzer
IRelogger
ANALYSIS_CALLBACKS
RELOG_CALLBACKS
Zestaw SDK analizy kompilacji języka C++ udostępnia zdarzenia za pomocą funkcji wywołania zwrotnego. W języku C++zaimplementuj funkcje wywołania zwrotnego, tworząc analizator lub klasę relogger, która dziedziczy interfejs IAnalyzer lub IRelogger. W języku C zaimplementuj wywołania zwrotne w funkcjach globalnych i podaj wskaźniki do nich w strukturze ANALYSIS_CALLBACKS lub RELOG_CALLBACKS.
Tworzenie grup MakeStaticAnalyzerGroup
MakeStaticReloggerGroup
MakeDynamicAnalyzerGroup
MakeDynamicReloggerGroup
Interfejs API języka C++ udostępnia funkcje i typy pomocnicze do grupowania wielu analizatorów i ponownego logowania obiektów. Grupy to schludny sposób dzielenia złożonej analizy na prostsze kroki. Vcperf jest zorganizowany w ten sposób.
Analizowanie lub ponowne rejestrowanie Analiza
Ponowne logowanie
AnalyzeA
AnalizaW
Ponowne logowanie
Ponowne logowanie

Analizowanie i ponowne rejestrowanie

Korzystanie ze śledzenia odbywa się za pośrednictwem sesji analizy lub sesji ponownego rejestrowania.

Korzystanie z regularnej analizy jest odpowiednie w przypadku większości scenariuszy. Ta metoda zapewnia elastyczność wyboru formatu wyjściowego: printf tekst, xml, JSON, baza danych, wywołania REST itd.

Rejestrowanie jest przeznaczone do analiz specjalnych, które muszą utworzyć plik wyjściowy ETW. Za pomocą rejestrowania można przetłumaczyć zdarzenia szczegółowych informacji o kompilacji języka C++ na własny format zdarzeń ETW. Odpowiednim zastosowaniem rejestrowania jest podłączanie danych szczegółowych informacji o kompilacji języka C++ do istniejących narzędzi i infrastruktury ETW. Na przykład vcperf korzysta z interfejsów ponownego rejestrowania. Dzieje się tak, ponieważ musi ona generować dane, które może zrozumieć Analizator wydajności systemu Windows, narzędzie ETW. Jeśli planujesz korzystanie z interfejsów ponownego rejestrowania, wymagana jest wcześniejsza wiedza na temat działania funkcji ETW.

Tworzenie grup analizatorów

Ważne jest, aby wiedzieć, jak tworzyć grupy. Oto przykład pokazujący, jak utworzyć grupę analizatora, która wyświetla komunikat Hello, world! dla każdego odbieranego zdarzenia rozpoczęcia działania.

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

Używanie zdarzeń

Funkcje Interfejs API języka C++ C API Uwagi
Dopasowywanie i filtrowanie zdarzeń MatchEventStackInMemberFunction
MatchEventStack
MatchEventInMemberFunction
MatchEvent
Interfejs API języka C++ oferuje funkcje, które ułatwiają wyodrębnianie zdarzeń, o których dbasz, ze śledzenia. W przypadku interfejsu API języka C filtrowanie musi być wykonywane ręcznie.
Typy danych zdarzeń Activity
BackEndPass
Dolna
C1DLL
C2DLL
Generowanie kodu
Wiersz polecenia
Kompilator
KompilatorPass
Zmienna środowiskowa
Zdarzenie
EventGroup
EventStack
WykonywalnyImageOutput
ExpOutput
FileInput
FileOutput
ForceInlinee
Plik FrontEnd
FrontEndFileGroup
FrontEndPass
Funkcja
HeaderUnit
ImpLibOutput
Wywołania
InvocationGroup
LibOutput
Linker
Grupa konsolidatora
LinkerPass
LTCG
Moduł
ObjOutput
OptICF
OptLBR
OptRef
Pass1
Pass2
PrekompiledHeader
PreLTCGOptRef
SimpleEvent
SymbolName
Klasa TemplateInstantiation
TemplateInstantiationGroup
Nitka
Od góry do góry
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

Działania i proste zdarzenia

Zdarzenia są dostępne w dwóch kategoriach: działania i proste zdarzenia. Działania to trwające procesy w czasie, które mają początek i koniec. Proste zdarzenia są wystąpieniami interpunkcyjnymi i nie mają czasu trwania. Podczas analizowania śladów MSVC przy użyciu zestawu SDK usługi Build Insights języka C++ otrzymasz oddzielne zdarzenia po uruchomieniu i zatrzymaniu działania. Po wystąpieniu prostego zdarzenia otrzymasz tylko jedno zdarzenie.

Relacje nadrzędny-podrzędny

Działania i proste zdarzenia są ze sobą powiązane za pośrednictwem relacji nadrzędny-podrzędny. Element nadrzędny działania lub prostego zdarzenia jest obejmującym działaniem, w którym występują. Na przykład podczas kompilowania pliku źródłowego kompilator musi przeanalizować plik, a następnie wygenerować kod. Działania analizy i generowania kodu są elementami podrzędnymi działania kompilatora.

Proste zdarzenia nie mają czasu trwania, więc nic innego nie może się w nich zdarzyć. W związku z tym nigdy nie mają żadnych dzieci.

Relacje nadrzędny-podrzędny każdego działania i prostego zdarzenia są wskazywane w tabeli zdarzeń. Znajomość tych relacji jest ważna podczas korzystania ze zdarzeń szczegółowych informacji o kompilacji języka C++. Często trzeba będzie polegać na nich, aby zrozumieć pełny kontekst zdarzenia.

Właściwości

Wszystkie zdarzenia mają następujące właściwości:

Właściwości opis
Identyfikator typu Liczba, która jednoznacznie identyfikuje typ zdarzenia.
Identyfikator wystąpienia Liczba, która jednoznacznie identyfikuje zdarzenie w ramach śledzenia. Jeśli w śladzie wystąpią dwa zdarzenia tego samego typu, oba uzyskaj unikatowy identyfikator wystąpienia.
Godzina rozpoczęcia Czas rozpoczęcia działania lub czas wystąpienia prostego zdarzenia.
Identyfikator procesu Liczba identyfikująca proces, w którym wystąpiło zdarzenie.
Identyfikator wątku Liczba identyfikująca wątek, w którym wystąpiło zdarzenie.
Indeks procesora Indeks oparty na zera wskazujący, który procesor logiczny był emitowany przez zdarzenie.
Nazwa zdarzenia Ciąg opisujący typ zdarzenia.

Wszystkie działania inne niż proste zdarzenia mają również następujące właściwości:

Właściwości opis
Czas zatrzymania Czas zatrzymania działania.
Wyłączny czas trwania Czas spędzony w działaniu, z wyłączeniem czasu spędzonego w działaniach podrzędnych.
Czas procesora CPU Czas, przez jaki procesor spędził wykonywanie kodu w wątku dołączonym do działania. Nie obejmuje czasu, kiedy wątek dołączony do działania spał.
Wyłączny czas procesora CPU Tak samo jak czas procesora CPU, ale z wyłączeniem czasu procesora CPU spędzonego przez działania podrzędne.
Czas zegara ściany Wkład działania do ogólnego czasu zegara ściany. Czas zegara ściany uwzględnia równoległość między działaniami. Załóżmy na przykład, że dwa niepowiązane działania działają równolegle. Oba mają czas trwania 10 sekund i dokładnie ten sam czas rozpoczęcia i zatrzymania. W takim przypadku usługa Build Insights przypisuje odpowiedzialność za zegar ścienny 5 sekund. W przeciwieństwie do tego, jeśli te działania są uruchamiane jeden po drugim bez nakładania się, są one przypisane odpowiedzialność zegara ściany 10 sekund.
Wyłączna odpowiedzialność za czas zegara ściany Tak samo jak odpowiedzialność za czas zegara ściany, ale wyklucza zegar odpowiedzialność za działania dzieci.

Niektóre zdarzenia mają własne właściwości poza wspomnianymi. W tym przypadku te dodatkowe właściwości są wymienione w tabeli zdarzeń.

Korzystanie z zdarzeń udostępnianych przez zestaw SDK szczegółowych informacji o kompilacji języka C++

Stos zdarzeń

Za każdym razem, gdy zestaw SDK usługi Build Insights języka C++ daje zdarzenie, jest on dostępny w postaci stosu. Ostatnim wpisem w stosie jest bieżące zdarzenie i wpisy przed jego hierarchią nadrzędną. Na przykład zdarzenia uruchamiania i zatrzymywania LTCG występują podczas przekazywania 1 konsolidatora. W takim przypadku otrzymany stos zawiera: [LINKER, PASS1, LTCG]. Hierarchia nadrzędna jest wygodna, ponieważ można prześledzić zdarzenie z powrotem do katalogu głównego. Jeśli działanie LTCG wymienione powyżej jest powolne, możesz natychmiast dowiedzieć się, które wywołanie konsolidatora było zaangażowane.

Pasujące zdarzenia i stosy zdarzeń

Zestaw SDK szczegółowych informacji o kompilacji języka C++ zapewnia każde zdarzenie w śladzie, ale przez większość czasu zależy tylko na podzestawie. W niektórych przypadkach można dbać tylko o podzbiór stosów zdarzeń. Zestaw SDK udostępnia obiekty ułatwiające szybkie wyodrębnianie potrzebnych zdarzeń lub stosu zdarzeń oraz odrzucanie tych, których nie potrzebujesz. Odbywa się to za pomocą tych pasujących funkcji:

Function opis
MatchEvent Zachowaj zdarzenie, jeśli jest zgodne z jednym z określonych typów. Prześlij zdarzenia dopasowane do typu lambda lub innego typu wywoływanego. Hierarchia nadrzędna zdarzenia nie jest brana pod uwagę przez tę funkcję.
MatchEventInMemberFunction Zachowaj zdarzenie, jeśli jest zgodne z typem określonym w parametrze funkcji składowej. Przekaż zdarzenia dopasowane do funkcji składowej. Hierarchia nadrzędna zdarzenia nie jest brana pod uwagę przez tę funkcję.
MatchEventStack Zachowaj zdarzenie, jeśli zarówno zdarzenie, jak i jego hierarchia nadrzędna są zgodne z określonymi typami. Przekaż zdarzenie i dopasowane zdarzenia hierarchii nadrzędnej do typu lambda lub innego typu wywoływanego.
MatchEventStackInMemberFunction Zachowaj zdarzenie, jeśli zarówno zdarzenie, jak i jego hierarchia nadrzędna są zgodne z typami określonymi na liście parametrów funkcji składowej. Przekaż zdarzenie i dopasowane zdarzenia hierarchii nadrzędnej do funkcji składowej.

Funkcje dopasowywania stosu zdarzeń, takie jak MatchEventStack zezwalają na luki podczas opisywania hierarchii nadrzędnej do dopasowania. Możesz na przykład powiedzieć, że interesuje Cię stos [LINKER, LTCG]. Pasuje również do stosu [LINKER, PASS1, LTCG]. Ostatni określony typ musi być typem zdarzenia, który ma być zgodny i nie jest częścią hierarchii nadrzędnej.

Klasy przechwytywania

Match* Korzystanie z funkcji wymaga określenia typów, które mają być zgodne. Te typy są wybierane z listy klas przechwytywania. Klasy przechwytywania znajdują się w kilku kategoriach opisanych poniżej.

Kategoria opis
Exact Te klasy przechwytywania są używane do dopasowania określonego typu zdarzenia i żadnego innego. Przykładem jest klasa kompilatora zgodna ze zdarzeniem COMPILER.
Symbole wieloznaczne Te klasy przechwytywania mogą służyć do dopasowania dowolnego zdarzenia z listy zdarzeń, które obsługują. Na przykład symbol wieloznaczny Działania pasuje do dowolnego zdarzenia działania. Innym przykładem jest symbol wieloznaczny CompilerPass , który może być zgodny z FRONT_END_PASS lub zdarzeniem BACK_END_PASS .
Grupuj Nazwy klas przechwytywania grup kończą się na grupie. Są one używane do dopasowywania wielu zdarzeń tego samego typu w wierszu, ignorując luki. Mają one sens tylko podczas dopasowywania zdarzeń cyklicznych, ponieważ nie wiesz, ile istnieje w stosie zdarzeń. Na przykład działanie FRONT_END_FILE odbywa się za każdym razem, gdy kompilator analizuje plik. To działanie jest cykliczne, ponieważ kompilator może znaleźć dyrektywę include podczas analizowania pliku. Klasa FrontEndFile pasuje tylko do jednego zdarzenia FRONT_END_FILE w stosie. Użyj klasy FrontEndFileGroup, aby dopasować całą hierarchię dołączania.
Grupa symboli wieloznacznych Grupa symboli wieloznacznych łączy właściwości symboli wieloznacznych i grup. Jedyną klasą tej kategorii jest InvocationGroup, która odpowiada i przechwytuje wszystkie zdarzenia KONSOLIDATOR i COMPILER w jednym stosie zdarzeń.

Zapoznaj się z tabelą zdarzeń, aby dowiedzieć się, które klasy przechwytywania mogą być używane do dopasowywania poszczególnych zdarzeń.

Po dopasowaniu: używanie przechwyconych zdarzeń

Po pomyślnym Match* zakończeniu dopasowania funkcje skonstruują obiekty klasy przechwytywania i przekazują je do określonej funkcji. Użyj tych obiektów klasy capture, aby uzyskać dostęp do właściwości zdarzeń.

Przykład

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