1+ #pragma once
2+
3+ #include < unordered_set>
4+ #include < cassert>
5+ #include " VcperfBuildInsights.h"
6+
7+ namespace vcperf
8+ {
9+
10+ // Linker invocations sometimes restart themselves. This can happen in the
11+ // following cases:
12+ //
13+ // 1. The /LTCG switch was not used, and an object compiled with /GL was found.
14+ // The linker is restarted with /LTCG.
15+ // 2. A 32-bit linker runs out of memory address space. The linker is restarted
16+ // in 64-bit.
17+ //
18+ // These restarts may have a non-negligible impact on throughput so we would
19+ // like to identify them automatically. The following C++ Build Insights
20+ // analyzer implements this functionality.
21+ class RestartedLinkerDetector : public BI ::IAnalyzer
22+ {
23+ public:
24+ RestartedLinkerDetector ():
25+ pass_{0 },
26+ restartedLinkers_{}
27+ {}
28+
29+ // Some analyses are done in multiple passes. The OnBeginAnalysisPass
30+ // function comes from the IAnalyzer interface, and will be called every
31+ // time a pass begins.
32+ BI::AnalysisControl OnBeginAnalysisPass () override
33+ {
34+ // Let's keep track of the pass we are in.
35+ ++pass_;
36+
37+ // We return CONTINUE to pass control over to the next
38+ // analyzer in the analysis chain.
39+ return BI::AnalysisControl::CONTINUE;
40+ }
41+
42+ // The OnStartActivity function will be called every time an activity start
43+ // event is seen. An activity is something happening in MSVC that has a
44+ // start and a stop time. The list of activities that generate start
45+ // events can be found in the Microsoft::Cpp::BuildInsights::Activities
46+ // namespace.
47+ BI::AnalysisControl OnStartActivity (const BI::EventStack& eventStack) override
48+ {
49+ // This analyzer is only active in the first pass. During this pass,
50+ // it will remember which linkers have been restarted by storing the
51+ // information in a cache.
52+ if (pass_ > 1 ) return BI::AnalysisControl::CONTINUE;
53+
54+ // BACKGROUND:
55+ //
56+ // C++ Build Insights events are related to each other in the
57+ // form of a graph. Nodes are either 'activities' which have a start
58+ // and stop time, or 'simple events' which are punctual. Activity nodes
59+ // may have children nodes. Simple events are always leaves. When an
60+ // activity has a child node, it means that the child event occurred
61+ // while the parent was still ongoing. For example, a Linker activity
62+ // may encapsulate the LinkerPass2 activity, which itself may
63+ // encapsulate a LinkerLTCG activity. In this case, the graph would have
64+ // a branch that looks like: Linker --> LinkerPass2 --> LinkerLTCG.
65+ //
66+ // EVENT STACKS:
67+ //
68+ // The 'eventStack' parameter contains the path in the graph for the
69+ // current event. Using the example above, if the current event is the
70+ // start of LinkerPass2, the event stack would contain:
71+ // [Linker, LinkerPass2], where LinkerPass2 is at the top of the stack.
72+ // You can think of the event stack as a call stack, because it tells
73+ // you what MSVC was currently executing, as well as how it got there.
74+ //
75+ // IDENTIFY RELEVANT BRANCHES:
76+ //
77+ // When writing a C++ Build Insights analyzer, the first thing you will
78+ // typically want to do is identify the branches in the graph that are
79+ // relevant to your analysis. In our case, we want to identify linkers
80+ // that are being restarted (i.e. linkers that are encapsulated by
81+ // another linker). This can be represented by a branch of this
82+ // form: X --> Linker --> X --> Linker, where the X's are activities we
83+ // don't care about.
84+ //
85+ // MATCHING A BRANCH
86+ //
87+ // Once the branch of interest has been identified, the next step is to
88+ // match the event stack against it. Here, this is done using
89+ // MatchEventStackInMemberFunction, which looks at a member function's
90+ // parameters to determine the branch to match. The last parameter in
91+ // the list is always the leaf, while others can be anywhere along the
92+ // path from the root. In this case we are matching the stack in the
93+ // FlagRestartedLinker member function. Its parameters describe a branch
94+ // that ends in a Linker and that has a parent Linker somewhere along
95+ // the path. This is exactly what our analysis needs.
96+ BI::MatchEventStackInMemberFunction (eventStack, this ,
97+ &RestartedLinkerDetector::FlagRestartedLinker);
98+
99+ return BI::AnalysisControl::CONTINUE;
100+ }
101+
102+ void FlagRestartedLinker (const A::Linker& parent, const A::Linker& child)
103+ {
104+ // Flag the parent linker as having been restarted
105+ // by inserting its instance ID into our cache.
106+ // In a given trace, each C++ Build Insights activity
107+ // or simple event has a unique instance ID. This ID is
108+ // useful to correlate events between different analysis
109+ // passes for the same trace.
110+ restartedLinkers_.insert (parent.EventInstanceId ());
111+ }
112+
113+ // This function is intended to be called by other analyzers
114+ // in the chain if they need to know whether a linker was
115+ // restarted. Restarted linkers have already been flagged in pass 1,
116+ // so we look into the cache to gather the information.
117+ bool WasLinkerRestarted (const A::Invocation& linker) const
118+ {
119+ return restartedLinkers_.find (linker.EventInstanceId ()) !=
120+ restartedLinkers_.end ();
121+ }
122+
123+ private:
124+ unsigned pass_;
125+ std::unordered_set<unsigned long long > restartedLinkers_;
126+ };
127+
128+ } // namespace vcperf
0 commit comments