1 //===- PassTimingInfo.cpp - LLVM Pass Timing Implementation ---------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file implements the LLVM Pass Timing infrastructure for both
10 // new and legacy pass managers.
11 //
12 // PassTimingInfo Class - This class is used to calculate information about the
13 // amount of time each pass takes to execute.  This only happens when
14 // -time-passes is enabled on the command line.
15 //
16 //===----------------------------------------------------------------------===//
17 
18 #include "llvm/IR/PassTimingInfo.h"
19 #include "llvm/ADT/Statistic.h"
20 #include "llvm/IR/PassInstrumentation.h"
21 #include "llvm/Pass.h"
22 #include "llvm/Support/CommandLine.h"
23 #include "llvm/Support/Debug.h"
24 #include "llvm/Support/FormatVariadic.h"
25 #include "llvm/Support/ManagedStatic.h"
26 #include "llvm/Support/Mutex.h"
27 #include "llvm/Support/TypeName.h"
28 #include "llvm/Support/raw_ostream.h"
29 #include <string>
30 
31 using namespace llvm;
32 
33 #define DEBUG_TYPE "time-passes"
34 
35 namespace llvm {
36 
37 bool TimePassesIsEnabled = false;
38 
39 static cl::opt<bool, true> EnableTiming(
40     "time-passes", cl::location(TimePassesIsEnabled), cl::Hidden,
41     cl::desc("Time each pass, printing elapsed time for each on exit"));
42 
43 namespace {
44 namespace legacy {
45 
46 //===----------------------------------------------------------------------===//
47 // Legacy pass manager's PassTimingInfo implementation
48 
49 /// Provides an interface for collecting pass timing information.
50 ///
51 /// It was intended to be generic but now we decided to split
52 /// interfaces completely. This is now exclusively for legacy-pass-manager use.
53 class PassTimingInfo {
54 public:
55   using PassInstanceID = void *;
56 
57 private:
58   StringMap<unsigned> PassIDCountMap; ///< Map that counts instances of passes
59   DenseMap<PassInstanceID, std::unique_ptr<Timer>> TimingData; ///< timers for pass instances
60   TimerGroup TG;
61 
62 public:
63   /// Default constructor for yet-inactive timeinfo.
64   /// Use \p init() to activate it.
65   PassTimingInfo();
66 
67   /// Print out timing information and release timers.
68   ~PassTimingInfo();
69 
70   /// Initializes the static \p TheTimeInfo member to a non-null value when
71   /// -time-passes is enabled. Leaves it null otherwise.
72   ///
73   /// This method may be called multiple times.
74   static void init();
75 
76   /// Prints out timing information and then resets the timers.
77   /// By default it uses the stream created by CreateInfoOutputFile().
78   void print(raw_ostream *OutStream = nullptr);
79 
80   /// Returns the timer for the specified pass if it exists.
81   Timer *getPassTimer(Pass *, PassInstanceID);
82 
83   static PassTimingInfo *TheTimeInfo;
84 
85 private:
86   Timer *newPassTimer(StringRef PassID, StringRef PassDesc);
87 };
88 
89 static ManagedStatic<sys::SmartMutex<true>> TimingInfoMutex;
90 
91 PassTimingInfo::PassTimingInfo()
92     : TG("pass", "... Pass execution timing report ...") {}
93 
94 PassTimingInfo::~PassTimingInfo() {
95   // Deleting the timers accumulates their info into the TG member.
96   // Then TG member is (implicitly) deleted, actually printing the report.
97   TimingData.clear();
98 }
99 
100 void PassTimingInfo::init() {
101   if (!TimePassesIsEnabled || TheTimeInfo)
102     return;
103 
104   // Constructed the first time this is called, iff -time-passes is enabled.
105   // This guarantees that the object will be constructed after static globals,
106   // thus it will be destroyed before them.
107   static ManagedStatic<PassTimingInfo> TTI;
108   TheTimeInfo = &*TTI;
109 }
110 
111 /// Prints out timing information and then resets the timers.
112 void PassTimingInfo::print(raw_ostream *OutStream) {
113   TG.print(OutStream ? *OutStream : *CreateInfoOutputFile(), true);
114 }
115 
116 Timer *PassTimingInfo::newPassTimer(StringRef PassID, StringRef PassDesc) {
117   unsigned &num = PassIDCountMap[PassID];
118   num++;
119   // Appending description with a pass-instance number for all but the first one
120   std::string PassDescNumbered =
121       num <= 1 ? PassDesc.str() : formatv("{0} #{1}", PassDesc, num).str();
122   return new Timer(PassID, PassDescNumbered, TG);
123 }
124 
125 Timer *PassTimingInfo::getPassTimer(Pass *P, PassInstanceID Pass) {
126   if (P->getAsPMDataManager())
127     return nullptr;
128 
129   init();
130   sys::SmartScopedLock<true> Lock(*TimingInfoMutex);
131   std::unique_ptr<Timer> &T = TimingData[Pass];
132 
133   if (!T) {
134     StringRef PassName = P->getPassName();
135     StringRef PassArgument;
136     if (const PassInfo *PI = Pass::lookupPassInfo(P->getPassID()))
137       PassArgument = PI->getPassArgument();
138     T.reset(newPassTimer(PassArgument.empty() ? PassName : PassArgument, PassName));
139   }
140   return T.get();
141 }
142 
143 PassTimingInfo *PassTimingInfo::TheTimeInfo;
144 } // namespace legacy
145 } // namespace
146 
147 Timer *getPassTimer(Pass *P) {
148   legacy::PassTimingInfo::init();
149   if (legacy::PassTimingInfo::TheTimeInfo)
150     return legacy::PassTimingInfo::TheTimeInfo->getPassTimer(P, P);
151   return nullptr;
152 }
153 
154 /// If timing is enabled, report the times collected up to now and then reset
155 /// them.
156 void reportAndResetTimings(raw_ostream *OutStream) {
157   if (legacy::PassTimingInfo::TheTimeInfo)
158     legacy::PassTimingInfo::TheTimeInfo->print(OutStream);
159 }
160 
161 //===----------------------------------------------------------------------===//
162 // Pass timing handling for the New Pass Manager
163 //===----------------------------------------------------------------------===//
164 
165 /// Returns the timer for the specified pass invocation of \p PassID.
166 /// Each time it creates a new timer.
167 Timer &TimePassesHandler::getPassTimer(StringRef PassID) {
168   // Take a vector of Timers created for this \p PassID and append
169   // one more timer to it.
170   TimerVector &Timers = TimingData[PassID];
171   unsigned Count = Timers.size() + 1;
172 
173   std::string FullDesc = formatv("{0} #{1}", PassID, Count).str();
174 
175   Timer *T = new Timer(PassID, FullDesc, TG);
176   Timers.emplace_back(T);
177   assert(Count == Timers.size() && "sanity check");
178 
179   return *T;
180 }
181 
182 TimePassesHandler::TimePassesHandler(bool Enabled)
183     : TG("pass", "... Pass execution timing report ..."), Enabled(Enabled) {}
184 
185 void TimePassesHandler::setOutStream(raw_ostream &Out) {
186   OutStream = &Out;
187 }
188 
189 void TimePassesHandler::print() {
190   if (!Enabled)
191     return;
192   TG.print(OutStream ? *OutStream : *CreateInfoOutputFile(), true);
193 }
194 
195 LLVM_DUMP_METHOD void TimePassesHandler::dump() const {
196   dbgs() << "Dumping timers for " << getTypeName<TimePassesHandler>()
197          << ":\n\tRunning:\n";
198   for (auto &I : TimingData) {
199     StringRef PassID = I.getKey();
200     const TimerVector& MyTimers = I.getValue();
201     for (unsigned idx = 0; idx < MyTimers.size(); idx++) {
202       const Timer* MyTimer = MyTimers[idx].get();
203       if (MyTimer && MyTimer->isRunning())
204         dbgs() << "\tTimer " << MyTimer << " for pass " << PassID << "(" << idx << ")\n";
205     }
206   }
207   dbgs() << "\tTriggered:\n";
208   for (auto &I : TimingData) {
209     StringRef PassID = I.getKey();
210     const TimerVector& MyTimers = I.getValue();
211     for (unsigned idx = 0; idx < MyTimers.size(); idx++) {
212       const Timer* MyTimer = MyTimers[idx].get();
213       if (MyTimer && MyTimer->hasTriggered() && !MyTimer->isRunning())
214         dbgs() << "\tTimer " << MyTimer << " for pass " << PassID << "(" << idx << ")\n";
215     }
216   }
217 }
218 
219 void TimePassesHandler::startTimer(StringRef PassID) {
220   Timer &MyTimer = getPassTimer(PassID);
221   TimerStack.push_back(&MyTimer);
222   if (!MyTimer.isRunning())
223     MyTimer.startTimer();
224 }
225 
226 void TimePassesHandler::stopTimer(StringRef PassID) {
227   assert(TimerStack.size() > 0 && "empty stack in popTimer");
228   Timer *MyTimer = TimerStack.pop_back_val();
229   assert(MyTimer && "timer should be present");
230   if (MyTimer->isRunning())
231     MyTimer->stopTimer();
232 }
233 
234 void TimePassesHandler::runBeforePass(StringRef PassID) {
235   if (isSpecialPass(PassID,
236                     {"PassManager", "PassAdaptor", "AnalysisManagerProxy"}))
237     return;
238 
239   startTimer(PassID);
240 
241   LLVM_DEBUG(dbgs() << "after runBeforePass(" << PassID << ")\n");
242   LLVM_DEBUG(dump());
243 }
244 
245 void TimePassesHandler::runAfterPass(StringRef PassID) {
246   if (isSpecialPass(PassID,
247                     {"PassManager", "PassAdaptor", "AnalysisManagerProxy"}))
248     return;
249 
250   stopTimer(PassID);
251 
252   LLVM_DEBUG(dbgs() << "after runAfterPass(" << PassID << ")\n");
253   LLVM_DEBUG(dump());
254 }
255 
256 void TimePassesHandler::registerCallbacks(PassInstrumentationCallbacks &PIC) {
257   if (!Enabled)
258     return;
259 
260   PIC.registerBeforeNonSkippedPassCallback(
261       [this](StringRef P, Any) { this->runBeforePass(P); });
262   PIC.registerAfterPassCallback(
263       [this](StringRef P, Any, const PreservedAnalyses &) {
264         this->runAfterPass(P);
265       });
266   PIC.registerAfterPassInvalidatedCallback(
267       [this](StringRef P, const PreservedAnalyses &) {
268         this->runAfterPass(P);
269       });
270   PIC.registerBeforeAnalysisCallback(
271       [this](StringRef P, Any) { this->runBeforePass(P); });
272   PIC.registerAfterAnalysisCallback(
273       [this](StringRef P, Any) { this->runAfterPass(P); });
274 }
275 
276 } // namespace llvm
277