From ba2dd59fa1ec43542be3cca3641156cd18dc98df Mon Sep 17 00:00:00 2001 From: Kevin Cadieux Date: Mon, 10 Feb 2020 23:19:00 -0800 Subject: [PATCH] Example of what can be done with the vcperf code base. --- src/Analyzers/ContextBuilder.cpp | 13 ++- src/Analyzers/ContextBuilder.h | 13 ++- src/Analyzers/RestartedLinkerDetector.h | 128 ++++++++++++++++++++++++ src/Commands.cpp | 44 ++++++-- src/Views/BuildExplorerView.cpp | 123 ++++++++++++++++++----- src/Views/BuildExplorerView.h | 56 ++++++++--- vcperf.vcxproj | 1 + vcperf.vcxproj.filters | 3 + 8 files changed, 328 insertions(+), 53 deletions(-) create mode 100644 src/Analyzers/RestartedLinkerDetector.h diff --git a/src/Analyzers/ContextBuilder.cpp b/src/Analyzers/ContextBuilder.cpp index 05a609a..179d850 100644 --- a/src/Analyzers/ContextBuilder.cpp +++ b/src/Analyzers/ContextBuilder.cpp @@ -226,6 +226,15 @@ void ContextBuilder::OnInvocation(const Invocation& invocation) const wchar_t* wTool = invocation.Type() == Invocation::Type::LINK ? L"Link" : L"CL"; + // Modify the Invocation Description string to indicate whether a linker + // was restarted. The Invocation description string is what is used to + // populate the Invocation Description column of the Build Explorer view. + const wchar_t* wRestartedLabel = L""; + + if (restartedLinkerDetector_->WasLinkerRestarted(invocation)) { + wRestartedLabel = L"Restarted "; + } + std::wstring invocationIdString = std::to_wstring(invocation.InvocationId()); unsigned long long instanceId = invocation.EventInstanceId(); @@ -237,14 +246,14 @@ void ContextBuilder::OnInvocation(const Invocation& invocation) std::wstring component = L"<" + std::wstring{wTool} + L" Invocation " + invocationIdString + L" Info>"; newContext.Component = CacheString(activeComponents_, instanceId, std::move(component)); - std::wstring invocationDescription = std::wstring{wTool} + L" Invocation " + invocationIdString; + std::wstring invocationDescription = std::wstring{wRestartedLabel} + wTool + L" Invocation " + invocationIdString; newContext.InvocationDescription = CacheString(invocationDescriptions_, instanceId, std::move(invocationDescription)); } else { newContext.Component = it->second.Path.c_str(); - std::wstring invocationDescription = std::wstring{wTool} + L" Invocation " + invocationIdString + + std::wstring invocationDescription = std::wstring{wRestartedLabel} + wTool + L" Invocation " + invocationIdString + L" (" + newContext.Component + L")"; newContext.InvocationDescription = CacheString(invocationDescriptions_, instanceId, std::move(invocationDescription)); } diff --git a/src/Analyzers/ContextBuilder.h b/src/Analyzers/ContextBuilder.h index 4013533..8bc85c1 100644 --- a/src/Analyzers/ContextBuilder.h +++ b/src/Analyzers/ContextBuilder.h @@ -10,6 +10,7 @@ #include "VcperfBuildInsights.h" #include "Utility.h" #include "PayloadBuilder.h" +#include "RestartedLinkerDetector.h" namespace vcperf { @@ -49,7 +50,7 @@ class ContextBuilder : public BI::IAnalyzer typedef std::unordered_map ContextLinkMap; public: - ContextBuilder() : + ContextBuilder(const RestartedLinkerDetector* restartedLinkerDetector) : analysisCount_{0}, analysisPass_{0}, timelineCount_{0}, @@ -61,7 +62,8 @@ class ContextBuilder : public BI::IAnalyzer invocationDescriptions_{}, timelineDescriptions_{}, currentContextData_{nullptr}, - currentInstanceId_{0} + currentInstanceId_{0}, + restartedLinkerDetector_{restartedLinkerDetector} { } @@ -194,6 +196,11 @@ class ContextBuilder : public BI::IAnalyzer ContextData* currentContextData_; unsigned long long currentInstanceId_; + + // This is a pointer to a RestartedLinkerDetector analyzer located + // earlier in the analysis chain. It will be used by the ContextBuilder + // to determine whether a linker has been restarted. + const RestartedLinkerDetector* restartedLinkerDetector_; }; -} // namespace vcperf \ No newline at end of file +} // namespace vcperf diff --git a/src/Analyzers/RestartedLinkerDetector.h b/src/Analyzers/RestartedLinkerDetector.h new file mode 100644 index 0000000..850c819 --- /dev/null +++ b/src/Analyzers/RestartedLinkerDetector.h @@ -0,0 +1,128 @@ +#pragma once + +#include +#include +#include "VcperfBuildInsights.h" + +namespace vcperf +{ + +// Linker invocations sometimes restart themselves. This can happen in the +// following cases: +// +// 1. The /LTCG switch was not used, and an object compiled with /GL was found. +// The linker is restarted with /LTCG. +// 2. A 32-bit linker runs out of memory address space. The linker is restarted +// in 64-bit. +// +// These restarts may have a non-negligible impact on throughput so we would +// like to identify them automatically. The following C++ Build Insights +// analyzer implements this functionality. +class RestartedLinkerDetector : public BI::IAnalyzer +{ +public: + RestartedLinkerDetector(): + pass_{0}, + restartedLinkers_{} + {} + + // Some analyses are done in multiple passes. The OnBeginAnalysisPass + // function comes from the IAnalyzer interface, and will be called every + // time a pass begins. + BI::AnalysisControl OnBeginAnalysisPass() override + { + // Let's keep track of the pass we are in. + ++pass_; + + // We return CONTINUE to pass control over to the next + // analyzer in the analysis chain. + return BI::AnalysisControl::CONTINUE; + } + + // The OnStartActivity function will be called every time an activity start + // event is seen. An activity is something happening in MSVC that has a + // start and a stop time. The list of activities that generate start + // events can be found in the Microsoft::Cpp::BuildInsights::Activities + // namespace. + BI::AnalysisControl OnStartActivity(const BI::EventStack& eventStack) override + { + // This analyzer is only active in the first pass. During this pass, + // it will remember which linkers have been restarted by storing the + // information in a cache. + if (pass_ > 1) return BI::AnalysisControl::CONTINUE; + + // BACKGROUND: + // + // C++ Build Insights events are related to each other in the + // form of a graph. Nodes are either 'activities' which have a start + // and stop time, or 'simple events' which are punctual. Activity nodes + // may have children nodes. Simple events are always leaves. When an + // activity has a child node, it means that the child event occurred + // while the parent was still ongoing. For example, a Linker activity + // may encapsulate the LinkerPass2 activity, which itself may + // encapsulate a LinkerLTCG activity. In this case, the graph would have + // a branch that looks like: Linker --> LinkerPass2 --> LinkerLTCG. + // + // EVENT STACKS: + // + // The 'eventStack' parameter contains the path in the graph for the + // current event. Using the example above, if the current event is the + // start of LinkerPass2, the event stack would contain: + // [Linker, LinkerPass2], where LinkerPass2 is at the top of the stack. + // You can think of the event stack as a call stack, because it tells + // you what MSVC was currently executing, as well as how it got there. + // + // IDENTIFY RELEVANT BRANCHES: + // + // When writing a C++ Build Insights analyzer, the first thing you will + // typically want to do is identify the branches in the graph that are + // relevant to your analysis. In our case, we want to identify linkers + // that are being restarted (i.e. linkers that are encapsulated by + // another linker). This can be represented by a branch of this + // form: X --> Linker --> X --> Linker, where the X's are activities we + // don't care about. + // + // MATCHING A BRANCH + // + // Once the branch of interest has been identified, the next step is to + // match the event stack against it. Here, this is done using + // MatchEventStackInMemberFunction, which looks at a member function's + // parameters to determine the branch to match. The last parameter in + // the list is always the leaf, while others can be anywhere along the + // path from the root. In this case we are matching the stack in the + // FlagRestartedLinker member function. Its parameters describe a branch + // that ends in a Linker and that has a parent Linker somewhere along + // the path. This is exactly what our analysis needs. + BI::MatchEventStackInMemberFunction(eventStack, this, + &RestartedLinkerDetector::FlagRestartedLinker); + + return BI::AnalysisControl::CONTINUE; + } + + void FlagRestartedLinker(const A::Linker& parent, const A::Linker& child) + { + // Flag the parent linker as having been restarted + // by inserting its instance ID into our cache. + // In a given trace, each C++ Build Insights activity + // or simple event has a unique instance ID. This ID is + // useful to correlate events between different analysis + // passes for the same trace. + restartedLinkers_.insert(parent.EventInstanceId()); + } + + // This function is intended to be called by other analyzers + // in the chain if they need to know whether a linker was + // restarted. Restarted linkers have already been flagged in pass 1, + // so we look into the cache to gather the information. + bool WasLinkerRestarted(const A::Invocation& linker) const + { + return restartedLinkers_.find(linker.EventInstanceId()) != + restartedLinkers_.end(); + } + +private: + unsigned pass_; + std::unordered_set restartedLinkers_; +}; + +} // namespace vcperf \ No newline at end of file diff --git a/src/Commands.cpp b/src/Commands.cpp index f3b3e9e..a9cf7f5 100644 --- a/src/Commands.cpp +++ b/src/Commands.cpp @@ -8,6 +8,7 @@ #include "Analyzers\ExpensiveTemplateInstantiationCache.h" #include "Analyzers\ContextBuilder.h" #include "Analyzers\MiscellaneousCache.h" +#include "Analyzers\RestartedLinkerDetector.h" #include "Views\BuildExplorerView.h" #include "Views\FunctionsView.h" #include "Views\FilesView.h" @@ -210,15 +211,42 @@ HRESULT DoStop(const std::wstring& sessionName, const std::filesystem::path& out { TRACING_SESSION_STATISTICS statistics{}; + // The C++ Build Insights SDK supports two types of analyses: + // + // 1. A regular analysis that simply receives each event in the trace in + // succession. The output of this analysis type is up to the author's + // discretion. This type of analysis makes use of a chain of analyzers + // that each take a turn in processing each event. Multipass analyses are + // allowed, in which the trace is passed through the analysis chain several + // times. + // 2. A relogging analysis which also receives each event in succession, but + // allows transforming and writing them back in a new ETW output trace. + // This type of analysis make use of two analyzer chains: + // + // a) A regular analyzer chain that runs before the relogging phase. + // This is useful if you want to precompute information before + // generating the output trace. Multipass analyses are allowed. + // b) A relogger chain which allows transforming and writing the + // events in a new trace. Only one pass is allowed. + // + // vcperf makes use of a relogging analysis. + // + // The RestartedLinkerDetector analyzer requires a prepass to cache + // the required information. We add it to the analyzer chain that runs + // before the relogging phase. + // + // Both the ContextBuilder and the BuildExplorerView query this analyzer + // so we pass a pointer to it in their constructors. ExpensiveTemplateInstantiationCache etic{analyzeTemplates}; - ContextBuilder cb; + RestartedLinkerDetector rld; + ContextBuilder cb{&rld}; MiscellaneousCache mc; - BuildExplorerView bev{&cb, &mc}; + BuildExplorerView bev{&rld, &cb, &mc}; FunctionsView funcv{&cb, &mc}; FilesView fv{&cb, &mc}; TemplateInstantiationsView tiv{&cb, &etic, &mc, analyzeTemplates}; - - auto analyzerGroup = MakeStaticAnalyzerGroup(&cb, &etic, &mc); + + auto analyzerGroup = MakeStaticAnalyzerGroup(&rld, &cb, &etic, &mc); auto reloggerGroup = MakeStaticReloggerGroup(&etic, &mc, &cb, &bev, &funcv, &fv, &tiv); std::wcout << L"Stopping and analyzing tracing session " << sessionName << L"..." << std::endl; @@ -272,15 +300,17 @@ HRESULT DoStopNoAnalyze(const std::wstring& sessionName, const std::filesystem:: HRESULT DoAnalyze(const std::filesystem::path& inputFile, const std::filesystem::path& outputFile, bool analyzeTemplates) { + // See above for an explanation of how this code works. ExpensiveTemplateInstantiationCache etic{analyzeTemplates}; - ContextBuilder cb; + RestartedLinkerDetector rld; + ContextBuilder cb{&rld}; MiscellaneousCache mc; - BuildExplorerView bev{&cb, &mc}; + BuildExplorerView bev{&rld, &cb, &mc}; FunctionsView funcv{&cb, &mc}; FilesView fv{&cb, &mc}; TemplateInstantiationsView tiv{&cb, &etic, &mc, analyzeTemplates}; - auto analyzerGroup = MakeStaticAnalyzerGroup(&cb, &etic, &mc); + auto analyzerGroup = MakeStaticAnalyzerGroup(&rld, &cb, &etic, &mc); auto reloggerGroup = MakeStaticReloggerGroup(&etic, &mc, &cb, &bev, &funcv, &fv, &tiv); std::wcout << L"Analyzing..." << std::endl; diff --git a/src/Views/BuildExplorerView.cpp b/src/Views/BuildExplorerView.cpp index 2107852..6ace7a9 100644 --- a/src/Views/BuildExplorerView.cpp +++ b/src/Views/BuildExplorerView.cpp @@ -2,6 +2,7 @@ #include "Utility.h" #include "CppBuildInsightsEtw.h" #include "PayloadBuilder.h" +#include "GenericFields.h" using namespace Microsoft::Cpp::BuildInsights; using namespace Activities; @@ -44,7 +45,8 @@ AnalysisControl BuildExplorerView::OnActivity(const EventStack& eventStack, return AnalysisControl::CONTINUE; } - LogActivity(relogSession, eventStack.Back()); + MatchEventStackInMemberFunction(eventStack, this, + &BuildExplorerView::OnGeneralActivity, relogSession); return AnalysisControl::CONTINUE; } @@ -70,20 +72,20 @@ AnalysisControl BuildExplorerView::OnSimpleEvent(const EventStack& eventStack, void BuildExplorerView::EmitInvocationEvents(const Invocation& invocation, const void* relogSession) { - LogActivity(relogSession, invocation); + LogActivity(relogSession, invocation, invocation); // For start events we also log invocaton properties such as version, working directory, etc... - ProcessStringProperty(relogSession, invocation, + ProcessStringProperty(relogSession, invocation, invocation, "Version", invocation.ToolVersionString()); // Tool path is not available for earlier versions of the toolset if (invocation.ToolPath()) { - ProcessStringProperty(relogSession, invocation, + ProcessStringProperty(relogSession, invocation, invocation, "ToolPath", invocation.ToolPath()); } - ProcessStringProperty(relogSession, invocation, + ProcessStringProperty(relogSession, invocation, invocation, "WorkingDirectory", invocation.WorkingDirectory()); } @@ -94,31 +96,36 @@ void BuildExplorerView::OnCompilerEnvironmentVariable(const Compiler& cl, if (_wcsicmp(name, L"CL") == 0) { - ProcessStringProperty(relogSession, envVar, "Env Var: CL", envVar.Value()); + ProcessStringProperty(relogSession, cl, envVar, "Env Var: CL", + envVar.Value()); return; } if (_wcsicmp(name, L"_CL_") == 0) { - ProcessStringProperty(relogSession, envVar, "Env Var: _CL_", envVar.Value()); + ProcessStringProperty(relogSession, cl, envVar, "Env Var: _CL_", + envVar.Value()); return; } if (_wcsicmp(name, L"INCLUDE") == 0) { - ProcessStringProperty(relogSession, envVar, "Env Var: INCLUDE", envVar.Value()); + ProcessStringProperty(relogSession, cl, envVar, "Env Var: INCLUDE", + envVar.Value()); return; } if (_wcsicmp(name, L"LIBPATH") == 0) { - ProcessStringProperty(relogSession, envVar, "Env Var: LIBPATH", envVar.Value()); + ProcessStringProperty(relogSession, cl, envVar, "Env Var: LIBPATH", + envVar.Value()); return; } if (_wcsicmp(name, L"PATH") == 0) { - ProcessStringProperty(relogSession, envVar, "Env Var: PATH", envVar.Value()); + ProcessStringProperty(relogSession, cl, envVar, "Env Var: PATH", + envVar.Value()); return; } } @@ -130,37 +137,88 @@ void BuildExplorerView::OnLinkerEnvironmentVariable(const Linker& link, if (_wcsicmp(name, L"LINK") == 0) { - ProcessStringProperty(relogSession, envVar, "Env Var: LINK", envVar.Value()); + ProcessStringProperty(relogSession, link, envVar, "Env Var: LINK", + envVar.Value()); return; } if (_wcsicmp(name, L"_LINK_") == 0) { - ProcessStringProperty(relogSession, envVar, "Env Var: _LINK_", envVar.Value()); + ProcessStringProperty(relogSession, link, envVar, "Env Var: _LINK_", + envVar.Value()); return; } if (_wcsicmp(name, L"LIB") == 0) { - ProcessStringProperty(relogSession, envVar, "Env Var: LIB", envVar.Value()); + ProcessStringProperty(relogSession, link, envVar, "Env Var: LIB", + envVar.Value()); return; } if (_wcsicmp(name, L"PATH") == 0) { - ProcessStringProperty(relogSession, envVar, "Env Var: PATH", envVar.Value()); + ProcessStringProperty(relogSession, link, envVar, "Env Var: PATH", + envVar.Value()); return; } if (_wcsicmp(name, L"TMP") == 0) { - ProcessStringProperty(relogSession, envVar, "Env Var: TMP", envVar.Value()); + ProcessStringProperty(relogSession, link, envVar, "Env Var: TMP", + envVar.Value()); return; } } +// This function classifies a given invocation into one of three categories: +// a compiler, a linker, or a restarted linker. +const char* BuildExplorerView::ClassifyInvocation(const Invocation& invocation) +{ + if (invocation.Type() == Invocation::Type::CL) { + return "Compiler"; + } + + // We use the RestartedLinkerDetector analyzer from + // RestartedLinkerDetector.h to determine whether the linker + // has been restarted. + if (restartedLinkerDetector_->WasLinkerRestarted(invocation)) { + return "Restarted Linker"; + } + + return "Linker"; +} + +// BACKGROUND: +// +// The C++ Build Insights WPA views, such as the Build Explorer, display their +// information in table form. These tables have columns which are described in +// the CppBuildInsightsEtw.xml ETW schema. In vcperf code, you can freely add +// rows to these tables by calling the InjectEvent function. However, you cannot +// add columns to them because the C++ Build Insights WPA add-in is closed-source +// and understands a fixed ETW schema. To get around this limitation, each table +// includes 'generic' columns that you can use to fill with your own data. +// Generic columns are filled by emitting a Generic Field event immediately +// after calling InjectEvent for the regular row event. If the table you are +// filling supports more than 1 generic field, such as the Build Explorer view, +// you can fill them in succession by emitting 2 or 3 Generic Field events one +// after the other. +// +// FUNCTION DESCRIPTION: +// +// This function emits the classification of a given invocation inside a +// Generic Field event. This allows grouping each row in the Build Explorer view +// based on whether it comes from a compiler, a linker, or a restarted linker. +// Use these groupings to quickly single out restarted linkers when viewing a +// trace in WPA. +void BuildExplorerView::EmitClassification(const Invocation& invocation, + const Event& subjectEntity, const void* relogSession) +{ + LogGenericStringField(ClassifyInvocation(invocation), subjectEntity, relogSession); +} + void BuildExplorerView::LogActivity(const void* relogSession, const Activity& a, - const char* activityName) + const Invocation& invocation, const char* activityName) { using std::chrono::duration_cast; using std::chrono::milliseconds; @@ -187,12 +245,18 @@ void BuildExplorerView::LogActivity(const void* relogSession, const Activity& a, InjectEvent(relogSession, &CppBuildInsightsGuid, desc, a.ProcessId(), a.ThreadId(), a.ProcessorIndex(), a.StartTimestamp(), p.GetData(), (unsigned long)p.Size()); + + // A generic field event needs to be emitted after each activity. + // This event contains the classification of the invocation that + // encapsulates this activity. + EmitClassification(invocation, a, relogSession); } template void BuildExplorerView::ProcessStringProperty(const void* relogSession, - const Event& e, const char* name, const TChar* value) + const Invocation& invocation, const Event& e, const char* name, + const TChar* value) { size_t len = std::char_traits::length(value); @@ -205,34 +269,36 @@ void BuildExplorerView::ProcessStringProperty(const void* relogSession, memcpy(segment, value, COMMAND_LINE_SEGMENT_LEN * sizeof(TChar)); segment[COMMAND_LINE_SEGMENT_LEN] = 0; - LogStringPropertySegment(relogSession, e, name, segment); + LogStringPropertySegment(relogSession, invocation, e, name, segment); len -= COMMAND_LINE_SEGMENT_LEN; value += COMMAND_LINE_SEGMENT_LEN; } - LogStringPropertySegment(relogSession, e, name, value); + LogStringPropertySegment(relogSession, invocation, e, name, value); } void BuildExplorerView::LogStringPropertySegment(const void* relogSession, - const Event& e, const char* name, const char* value) + const Invocation& invocation, const Event& e, const char* name, + const char* value) { - LogStringPropertySegment(relogSession, e, name, value, + LogStringPropertySegment(relogSession, invocation, e, name, value, &CppBuildInsightsBuildExplorerAnsiStringProperty); } void BuildExplorerView::LogStringPropertySegment(const void* relogSession, - const Event& e, const char* name, const wchar_t* value) + const Invocation& invocation, const Event& e, const char* name, + const wchar_t* value) { - LogStringPropertySegment(relogSession, e, name, value, + LogStringPropertySegment(relogSession, invocation, e, name, value, &CppBuildInsightsBuildExplorerUnicodeStringProperty); } template void BuildExplorerView::LogStringPropertySegment(const void* relogSession, - const Event& e, const char* name, const TChar* value, - PCEVENT_DESCRIPTOR desc) + const Invocation& invocation, const Event& e, const char* name, + const TChar* value, PCEVENT_DESCRIPTOR desc) { auto* context = contextBuilder_->GetContextData(); @@ -252,6 +318,11 @@ void BuildExplorerView::LogStringPropertySegment(const void* relogSession, InjectEvent(relogSession, &CppBuildInsightsGuid, desc, e.ProcessId(), e.ThreadId(), e.ProcessorIndex(), e.Timestamp(), p.GetData(), (unsigned long)p.Size()); + + // A generic field event needs to be emitted after each property segment. + // This event contains the classification of the invocation that this + // property belongs to. + EmitClassification(invocation, e, relogSession); } -} // namespace vcperf \ No newline at end of file +} // namespace vcperf diff --git a/src/Views/BuildExplorerView.h b/src/Views/BuildExplorerView.h index cfbb0e9..919d0ab 100644 --- a/src/Views/BuildExplorerView.h +++ b/src/Views/BuildExplorerView.h @@ -3,6 +3,7 @@ #include "VcperfBuildInsights.h" #include "Analyzers\ContextBuilder.h" #include "Analyzers\MiscellaneousCache.h" +#include "Analyzers\RestartedLinkerDetector.h" namespace vcperf { @@ -10,8 +11,11 @@ namespace vcperf class BuildExplorerView : public BI::IRelogger { public: - BuildExplorerView(ContextBuilder* contextBuilder, + BuildExplorerView( + const RestartedLinkerDetector* restartedLinkerDetector, + ContextBuilder* contextBuilder, MiscellaneousCache* miscellaneousCache) : + restartedLinkerDetector_{restartedLinkerDetector}, contextBuilder_{contextBuilder}, miscellaneousCache_{miscellaneousCache} {} @@ -30,20 +34,28 @@ class BuildExplorerView : public BI::IRelogger EmitInvocationEvents(invocation, relogSession); } - void OnCompilerPass(const A::CompilerPass& pass, const void* relogSession) + void OnCompilerPass(const A::Compiler& cl, const A::CompilerPass& pass, const void* relogSession) { - LogActivity(relogSession, pass); + LogActivity(relogSession, pass, cl); } - void OnThread(const A::Activity& a, const A::Thread& t, const void* relogSession) + void OnThread(const A::Invocation& invocation, const A::Activity& a, const A::Thread& t, + const void* relogSession) { - OnThreadActivity(a, t, relogSession); + OnThreadActivity(invocation, a, t, relogSession); } void OnCommandLine(const A::Invocation& invocation, const SE::CommandLine& commandLine, const void* relogSession) { - ProcessStringProperty(relogSession, commandLine, "CommandLine", commandLine.Value()); + ProcessStringProperty(relogSession, invocation, commandLine, + "CommandLine", commandLine.Value()); + } + + void OnGeneralActivity(const A::Invocation& invocation, const A::Activity& a, + const void* relogSession) + { + LogActivity(relogSession, a, invocation); } void OnCompilerEnvironmentVariable(const A::Compiler& cl, const SE::EnvironmentVariable& envVar, @@ -52,6 +64,10 @@ class BuildExplorerView : public BI::IRelogger void OnLinkerEnvironmentVariable(const A::Linker& link, const SE::EnvironmentVariable& envVar, const void* relogSession); + const char* ClassifyInvocation(const A::Invocation& invocation); + void EmitClassification(const A::Invocation& invocation, const BI::Event& subjectEvent, + const void* relogSession); + BI::AnalysisControl OnEndRelogging() override { return BI::AnalysisControl::CONTINUE; @@ -61,39 +77,49 @@ class BuildExplorerView : public BI::IRelogger BI::AnalysisControl OnActivity(const BI::EventStack& eventStack, const void* relogSession); - void OnThreadActivity(const A::Activity& a, const A::Thread& t, const void* relogSession) + void OnThreadActivity(const A::Invocation& invocation, const A::Activity& a, const A::Thread& t, + const void* relogSession) { std::string activityName = a.EventName(); activityName += "Thread"; - LogActivity(relogSession, t, activityName.c_str()); + LogActivity(relogSession, t, invocation, activityName.c_str()); } void EmitInvocationEvents(const A::Invocation& invocation, const void* relogSession); - void LogActivity(const void* relogSession, const A::Activity& a, const char* activityName); + void LogActivity(const void* relogSession, const A::Activity& a, const A::Invocation& invocation, + const char* activityName); - void LogActivity(const void* relogSession, const A::Activity& a) + void LogActivity(const void* relogSession, const A::Activity& a, const A::Invocation& invocation) { - LogActivity(relogSession, a, a.EventName()); + LogActivity(relogSession, a, invocation, a.EventName()); } template void ProcessStringProperty(const void* relogSession, - const BI::Event& e, const char* name, const TChar* value); + const A::Invocation& invocation, const BI::Event& e, + const char* name, const TChar* value); void LogStringPropertySegment(const void* relogSession, - const BI::Event& e, const char* name, const char* value); + const A::Invocation& invocation, const BI::Event& e, + const char* name, const char* value); void LogStringPropertySegment(const void* relogSession, - const BI::Event& e, const char* name, const wchar_t* value); + const A::Invocation& invocation, const BI::Event& e, + const char* name, const wchar_t* value); template - void LogStringPropertySegment(const void* relogSession, const BI::Event& e, + void LogStringPropertySegment(const void* relogSession, + const A::Invocation& invocation, const BI::Event& e, const char* name, const TChar* value, PCEVENT_DESCRIPTOR desc); std::wstring invocationInfoString_; + // This is a pointer to a RestartedLinkerDetector analyzer located + // earlier in the analysis chain. It will be used by BuildExplorerView + // to determine whether a linker has been restarted. + const RestartedLinkerDetector* restartedLinkerDetector_; ContextBuilder* contextBuilder_; MiscellaneousCache* miscellaneousCache_; }; diff --git a/vcperf.vcxproj b/vcperf.vcxproj index c25d893..c7719b0 100644 --- a/vcperf.vcxproj +++ b/vcperf.vcxproj @@ -192,6 +192,7 @@ + diff --git a/vcperf.vcxproj.filters b/vcperf.vcxproj.filters index db1dc01..cc84287 100644 --- a/vcperf.vcxproj.filters +++ b/vcperf.vcxproj.filters @@ -92,6 +92,9 @@ Header Files + + Header Files\Analyzers +