C++ 組建見解 SDK
C++ Build Insights SDK 與 Visual Studio 2017 和更新版本相容。 若要查看這些版本的檔,請將本文的 Visual Studio 版本 選取器控制項設定為 Visual Studio 2017 或更新版本。 其位於此頁面目錄頂端。
C++ Build Insights SDK 是 API 的集合,可讓您在 C++ Build Insights 平臺之上建立個人化工具。 此頁面提供高階概觀,可協助您開始使用。
取得 SDK
您可以遵循下列步驟,將 C++ Build Insights SDK 下載為 NuGet 套件:
- 從 Visual Studio 2017 和更新版本建立新的 C++ 專案。
- 在 [方案總管] 窗格中,以滑鼠右鍵按一下您的專案。
- 從操作功能表中選取 [管理 NuGet 套件 ]。
- 在右上方,選取 nuget.org 套件來源。
- 搜尋最新版的 Microsoft.Cpp.BuildInsights 套件。
- 選擇 [ 安裝 ]。
- 接受授權。
如需 SDK 周圍一般概念的相關資訊,請閱讀 。 您也可以存取官方 的 C++ Build Insights 範例 GitHub 存放庫 ,以查看使用 SDK 的實際 C++ 應用程式範例。
收集追蹤
使用 C++ Build Insights SDK 來分析 MSVC 工具鏈中傳出的事件時,必須先收集追蹤。 SDK 會使用 Windows 事件追蹤(ETW)作為基礎追蹤技術。 收集追蹤的方式有兩種:
方法 1:在 Visual Studio 2019 和更新版本中使用 vcperf
開啟 VS 2019 提升許可權的 x64 Native Tools 命令提示字元。
執行下列命令:
vcperf /start MySessionName
建立您的專案。
執行下列命令:
vcperf /stopnoanalyze MySessionName outputTraceFile.etl
重要
使用
/stopnoanalyze
vcperf 停止追蹤時使用 命令。 您無法使用 C++ Build Insights SDK 來分析由一般/stop
命令停止的追蹤。
方法 2:以程式設計方式
使用這些 C++ Build Insights SDK 追蹤集合函式,以程式設計方式啟動和停止追蹤。 執行這些函式呼叫的程式必須具有系統管理許可權。 只有啟動和停止追蹤功能需要系統管理許可權。 C++ Build Insights SDK 中的所有其他函式都可以執行,而不需要它們。
與追蹤集合相關的 SDK 函式
後續各節會示範如何設定分析或重新記錄會話。 合併的功能函式需要此功能,例如 StopAndAnalyzeTracingSession 。
取用追蹤
取得 ETW 追蹤之後,請使用 C++ Build Insights SDK 將其解壓縮。 SDK 提供事件格式,可讓您快速開發工具。 我們不建議您不使用 SDK 來取用原始 ETW 追蹤。 MSVC 所使用的事件格式未經記載、已優化以調整為大型組建,且難以理解。 此外,C++ Build Insights SDK API 是穩定的,而未經注意的原始 ETW 追蹤格式可能會變更。
與追蹤耗用量相關的 SDK 類型和函式
功能 | C++ API | C API | 備註 |
---|---|---|---|
設定事件回呼 | IAnalyzer IRelogger |
ANALYSIS_CALLBACKS RELOG_CALLBACKS |
C++ Build Insights SDK 會透過回呼函式提供事件。 在 C++ 中,建立繼承 IAnalyzer 或 IRelogger 介面的分析器或重新記錄器類別,以實作回呼函式。 在 C 中,在全域函式中實作回呼,並在ANALYSIS_CALLBACKS或RELOG_CALLBACKS結構中提供它們的指標。 |
建置群組 | MakeStaticAnalyzerGroup MakeStaticReloggerGroup MakeDynamicAnalyzerGroup MakeDynamicReloggerGroup |
C++ API 提供協助程式函式和類型,以將多個分析器和重新記錄器物件分組在一起。 群組是將複雜分析分割成更簡單步驟的整齊方式。 vcperf 會以這種方式組織。 | |
分析或重新記錄 | 分析 重新記錄 |
AnalyzeA AnalyzeW RelogA RelogW |
分析和重新記錄
取用追蹤是透過分析會話或重新記錄會話來完成。
使用一般分析適用于大部分案例。 此方法可讓您彈性地選擇輸出格式: printf
text、xml、JSON、database、REST 呼叫等等。
重新記錄適用于需要產生 ETW 輸出檔案的特殊用途分析。 使用重新記錄,您可以將 C++ Build Insights 事件轉譯成您自己的 ETW 事件格式。 適當的重新記錄用途是將 C++ Build Insights 資料連結至現有的 ETW 工具和基礎結構。 例如, vcperf 會使用重新記錄介面。 這是因為它必須產生 Windows 效能分析器,ETW 工具可以理解的資料。 如果您打算使用重新記錄介面,則需要一些先前知道 ETW 的運作方式。
建立分析器群組
請務必瞭解如何建立群組。 以下範例示範如何建立分析器群組,針對它收到的每個活動啟動事件,列印 Hello, world! 。
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;
}
使用事件
與事件相關的 SDK 類型和函式
活動和簡單事件
事件分為兩種類別: 活動和 簡單事件 。 活動是有開始和結束的時間進行中的程式。 簡單事件是標點專案,而且沒有持續時間。 使用 C++ Build Insights SDK 分析 MSVC 追蹤時,您會在活動啟動和停止時收到個別的事件。 當發生簡單事件時,您只會收到一個事件。
父子關聯性
活動和簡單事件會透過父子關聯性彼此關聯。 活動或簡單事件的父代是活動所在的內含活動。 例如,編譯來源檔案時,編譯器必須剖析檔案,然後產生程式碼。 剖析和程式碼產生活動都是編譯器活動的子系。
簡單的事件沒有持續時間,因此它們內不會發生任何其他事件。 因此,他們從來沒有任何孩子。
每個活動和簡單事件的父子關聯性都會在事件資料表 中 指出。 在取用 C++ Build Insights 事件時,瞭解這些關聯性非常重要。 您通常必須依賴它們來瞭解事件的完整內容。
屬性
所有事件都有下列屬性:
屬性 | 說明 |
---|---|
類型識別碼 | 可唯一識別事件種類的數位。 |
實例識別碼 | 可唯一識別追蹤內事件的數位。 如果追蹤中發生兩個相同類型的事件,兩者都會取得唯一的實例識別碼。 |
開始時間 | 活動開始的時間,或發生簡單事件的時間。 |
進程識別碼 | 數位,識別發生事件的進程。 |
執行緒識別碼 | 識別發生事件之執行緒的數位。 |
處理器索引 | 以零起始的索引,指出事件發出的邏輯處理器。 |
事件名稱 | 描述事件種類的字串。 |
簡單事件以外的所有活動也有下列屬性:
屬性 | 說明 |
---|---|
停止時間 | 活動停止的時間。 |
獨佔持續時間 | 活動所花費的時間,不包括其子活動所花費的時間。 |
CPU 時間 | CPU 在附加至活動的執行緒中執行程式碼所花費的時間。 它不包含附加至活動之執行緒睡眠的時間。 |
獨佔 CPU 時間 | 與 CPU 時間相同,但不包括子活動花費的 CPU 時間。 |
時鐘時間責任 | 活動對整體時鐘時間的貢獻。 時鐘時間責任會考慮活動之間的平行處理原則。 例如,假設兩個不相關的活動會平行執行。 兩者都有 10 秒的持續時間,而且完全相同的開始和停止時間。 在此情況下,Build Insights 會指派 5 秒的時鐘時間責任。 相反地,如果這些活動執行一個接一個且沒有重迭,則兩者都會被指派 10 秒的時鐘時間責任。 |
專屬的時鐘時間責任 | 與時鐘時間責任相同,但排除兒童活動的時鐘時間責任。 |
有些事件除了提及的事件之外,有自己的屬性。 在此情況下,這些額外的屬性會列在事件資料表 中 。
取用 C++ Build Insights SDK 所提供的事件
事件堆疊
每當 C++ Build Insights SDK 提供事件時,就會以堆疊的形式出現。 堆疊中的最後一個專案是目前事件,以及其父階層之前的專案。 例如, LTCG 啟動和停止事件會在連結器傳遞 1 期間發生。 在此情況下,您收到的堆疊包含:[ LINKER 、 PASS1 、LTCG]。 父階層很方便,因為您可以將事件追蹤到其根目錄。 如果上述 LTCG 活動緩慢,您可以立即瞭解涉及哪個連結器調用。
比對事件和事件堆疊
C++ Build Insights SDK 會提供追蹤中的每個事件,但大部分時候您只關心其中的子集。 在某些情況下,您可能只關心事件堆疊 的 子集。 SDK 提供可協助您快速擷取所需的事件或事件堆疊,並拒絕您不需要的事件或事件堆疊。 其可透過下列比對函式來完成:
函式 | 描述 |
---|---|
MatchEvent | 如果事件符合其中一個指定的類型,請保留事件。 將相符的事件轉送至 Lambda 或其他可呼叫類型。 此函式不會考慮事件的父階層。 |
MatchEventInMemberFunction | 如果事件符合成員函式參數中指定的類型,請保留事件。 將相符的事件轉送至成員函式。 此函式不會考慮事件的父階層。 |
MatchEventStack | 如果事件及其父階層都符合指定的類型,請保留事件。 將事件和相符的父階層事件轉送至 Lambda 或其他可呼叫類型。 |
MatchEventStackInMemberFunction | 如果事件及其父階層都符合成員函式參數清單中指定的類型,請保留事件。 將事件和相符的父階層事件轉送至成員函式。 |
事件堆疊比對函式,例如 MatchEventStack
在描述要比對的父階層時允許間距。 例如,您可以假設您有興趣 [ LINKER , LTCG ] 堆疊。 它也會比對 [LINKER, PASS1 , LTCG] 堆疊。 指定的最後一個類型必須是符合的事件種類,而且不是父階層的一部分。
擷取類別
使用 函 Match*
式需要您指定您想要比對的類型。 從擷取類別 清單中 選取這些類型。 擷取類別有數個類別,如下所述。
類別 | 描述 |
---|---|
精確 | 這些擷取類別可用來比對特定事件種類,而沒有其他類別。 例如,編譯器 類別符合 COMPILER 事件。 |
通 配 符 | 這些擷取類別可用來比對所支援事件清單中的任何事件。 例如, 活動 萬用字元符合任何活動事件。 另一個範例是 CompilerPass 萬用字元,它可以比對 FRONT_END_PASS 或 BACK_END_PASS 事件。 |
群組 | 群組擷取類別的名稱結尾為 Group 。 它們用來比對資料列中相同類型的多個事件,忽略間距。 它們只有在比對遞迴事件時才有意義,因為您不知道事件堆疊中有多少存在。 例如, 每次編譯器剖析檔案時,就會發生FRONT_END_FILE 活動。 此活動是遞迴的,因為編譯器在剖析檔案時可能會找到 include 指示詞。 FrontEndFile 類別只會比對堆疊中的一個FRONT_END_FILE事件。 使用 FrontEndFileGroup 類別來比對整個 Include 階層。 |
萬用字元群組 | 萬用字元群組會結合萬用字元和群組的屬性。 此類別的唯一類別是 InvocationGroup ,它會比對並擷取單一事件堆疊中的所有 LINKER 和 COMPILER 事件。 |
請參閱事件資料表 ,以瞭解哪些擷取類別可用來比對每個事件。
比對之後:使用擷取的事件
比對成功完成之後,函式會 Match*
建構擷取類別物件,並將其轉送至指定的函式。 使用這些擷取類別物件來存取事件的屬性。
範例
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;
}
}
意見反映
https://aka.ms/ContentUserFeedback。
即將推出:我們會在 2024 年淘汰 GitHub 問題,並以全新的意見反應系統取代並作為內容意見反應的渠道。 如需更多資訊,請參閱:提交及檢視以下的意見反映: