1 // Copyright 2016 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "status.h"
16
17 #ifdef _WIN32
18 #include "win32port.h"
19 #else
20 #ifndef __STDC_FORMAT_MACROS
21 #define __STDC_FORMAT_MACROS
22 #endif
23 #include <cinttypes>
24 #endif
25
26 #include <stdarg.h>
27 #include <stdlib.h>
28
29 #ifdef _WIN32
30 #include <fcntl.h>
31 #include <io.h>
32 #endif
33
34 #include "debug_flags.h"
35
36 using namespace std;
37
StatusPrinter(const BuildConfig & config)38 StatusPrinter::StatusPrinter(const BuildConfig& config)
39 : config_(config), started_edges_(0), finished_edges_(0), total_edges_(0),
40 running_edges_(0), progress_status_format_(NULL),
41 current_rate_(config.parallelism) {
42 // Don't do anything fancy in verbose mode.
43 if (config_.verbosity != BuildConfig::NORMAL)
44 printer_.set_smart_terminal(false);
45
46 progress_status_format_ = getenv("NINJA_STATUS");
47 if (!progress_status_format_)
48 progress_status_format_ = "[%f/%t] ";
49 }
50
EdgeAddedToPlan(const Edge * edge)51 void StatusPrinter::EdgeAddedToPlan(const Edge* edge) {
52 ++total_edges_;
53
54 // Do we know how long did this edge take last time?
55 if (edge->prev_elapsed_time_millis != -1) {
56 ++eta_predictable_edges_total_;
57 ++eta_predictable_edges_remaining_;
58 eta_predictable_cpu_time_total_millis_ += edge->prev_elapsed_time_millis;
59 eta_predictable_cpu_time_remaining_millis_ +=
60 edge->prev_elapsed_time_millis;
61 } else
62 ++eta_unpredictable_edges_remaining_;
63 }
64
EdgeRemovedFromPlan(const Edge * edge)65 void StatusPrinter::EdgeRemovedFromPlan(const Edge* edge) {
66 --total_edges_;
67
68 // Do we know how long did this edge take last time?
69 if (edge->prev_elapsed_time_millis != -1) {
70 --eta_predictable_edges_total_;
71 --eta_predictable_edges_remaining_;
72 eta_predictable_cpu_time_total_millis_ -= edge->prev_elapsed_time_millis;
73 eta_predictable_cpu_time_remaining_millis_ -=
74 edge->prev_elapsed_time_millis;
75 } else
76 --eta_unpredictable_edges_remaining_;
77 }
78
BuildEdgeStarted(const Edge * edge,int64_t start_time_millis)79 void StatusPrinter::BuildEdgeStarted(const Edge* edge,
80 int64_t start_time_millis) {
81 ++started_edges_;
82 ++running_edges_;
83 time_millis_ = start_time_millis;
84
85 if (edge->use_console() || printer_.is_smart_terminal())
86 PrintStatus(edge, start_time_millis);
87
88 if (edge->use_console())
89 printer_.SetConsoleLocked(true);
90 }
91
RecalculateProgressPrediction()92 void StatusPrinter::RecalculateProgressPrediction() {
93 time_predicted_percentage_ = 0.0;
94
95 // Sometimes, the previous and actual times may be wildly different.
96 // For example, the previous build may have been fully recovered from ccache,
97 // so it was blazing fast, while the new build no longer gets hits from ccache
98 // for whatever reason, so it actually compiles code, which takes much longer.
99 // We should detect such cases, and avoid using "wrong" previous times.
100
101 // Note that we will only use the previous times if there are edges with
102 // previous time knowledge remaining.
103 bool use_previous_times = eta_predictable_edges_remaining_ &&
104 eta_predictable_cpu_time_remaining_millis_;
105
106 // Iff we have sufficient statistical information for the current run,
107 // that is, if we have took at least 15 sec AND finished at least 5% of edges,
108 // we can check whether our performance so far matches the previous one.
109 if (use_previous_times && total_edges_ && finished_edges_ &&
110 (time_millis_ >= 15 * 1e3) &&
111 (((double)finished_edges_ / total_edges_) >= 0.05)) {
112 // Over the edges we've just run, how long did they take on average?
113 double actual_average_cpu_time_millis =
114 (double)cpu_time_millis_ / finished_edges_;
115 // What is the previous average, for the edges with such knowledge?
116 double previous_average_cpu_time_millis =
117 (double)eta_predictable_cpu_time_total_millis_ /
118 eta_predictable_edges_total_;
119
120 double ratio = std::max(previous_average_cpu_time_millis,
121 actual_average_cpu_time_millis) /
122 std::min(previous_average_cpu_time_millis,
123 actual_average_cpu_time_millis);
124
125 // Let's say that the average times should differ by less than 10x
126 use_previous_times = ratio < 10;
127 }
128
129 int edges_with_known_runtime = finished_edges_;
130 if (use_previous_times)
131 edges_with_known_runtime += eta_predictable_edges_remaining_;
132 if (edges_with_known_runtime == 0)
133 return;
134
135 int edges_with_unknown_runtime = use_previous_times
136 ? eta_unpredictable_edges_remaining_
137 : (total_edges_ - finished_edges_);
138
139 // Given the time elapsed on the edges we've just run,
140 // and the runtime of the edges for which we know previous runtime,
141 // what's the edge's average runtime?
142 int64_t edges_known_runtime_total_millis = cpu_time_millis_;
143 if (use_previous_times)
144 edges_known_runtime_total_millis +=
145 eta_predictable_cpu_time_remaining_millis_;
146
147 double average_cpu_time_millis =
148 (double)edges_known_runtime_total_millis / edges_with_known_runtime;
149
150 // For the edges for which we do not have the previous runtime,
151 // let's assume that their average runtime is the same as for the other edges,
152 // and we therefore can predict their remaining runtime.
153 double unpredictable_cpu_time_remaining_millis =
154 average_cpu_time_millis * edges_with_unknown_runtime;
155
156 // And therefore we can predict the remaining and total runtimes.
157 double total_cpu_time_remaining_millis =
158 unpredictable_cpu_time_remaining_millis;
159 if (use_previous_times)
160 total_cpu_time_remaining_millis +=
161 eta_predictable_cpu_time_remaining_millis_;
162 double total_cpu_time_millis =
163 cpu_time_millis_ + total_cpu_time_remaining_millis;
164 if (total_cpu_time_millis == 0.0)
165 return;
166
167 // After that we can tell how much work we've completed, in time units.
168 time_predicted_percentage_ = cpu_time_millis_ / total_cpu_time_millis;
169 }
170
BuildEdgeFinished(Edge * edge,int64_t start_time_millis,int64_t end_time_millis,bool success,const string & output)171 void StatusPrinter::BuildEdgeFinished(Edge* edge, int64_t start_time_millis,
172 int64_t end_time_millis, bool success,
173 const string& output) {
174 time_millis_ = end_time_millis;
175 ++finished_edges_;
176
177 int64_t elapsed = end_time_millis - start_time_millis;
178 cpu_time_millis_ += elapsed;
179
180 // Do we know how long did this edge take last time?
181 if (edge->prev_elapsed_time_millis != -1) {
182 --eta_predictable_edges_remaining_;
183 eta_predictable_cpu_time_remaining_millis_ -=
184 edge->prev_elapsed_time_millis;
185 } else
186 --eta_unpredictable_edges_remaining_;
187
188 if (edge->use_console())
189 printer_.SetConsoleLocked(false);
190
191 if (config_.verbosity == BuildConfig::QUIET)
192 return;
193
194 if (!edge->use_console())
195 PrintStatus(edge, end_time_millis);
196
197 --running_edges_;
198
199 // Print the command that is spewing before printing its output.
200 if (!success) {
201 string outputs;
202 for (vector<Node*>::const_iterator o = edge->outputs_.begin();
203 o != edge->outputs_.end(); ++o)
204 outputs += (*o)->path() + " ";
205
206 if (printer_.supports_color()) {
207 printer_.PrintOnNewLine("\x1B[31m" "FAILED: " "\x1B[0m" + outputs + "\n");
208 } else {
209 printer_.PrintOnNewLine("FAILED: " + outputs + "\n");
210 }
211 printer_.PrintOnNewLine(edge->EvaluateCommand() + "\n");
212 }
213
214 if (!output.empty()) {
215 // ninja sets stdout and stderr of subprocesses to a pipe, to be able to
216 // check if the output is empty. Some compilers, e.g. clang, check
217 // isatty(stderr) to decide if they should print colored output.
218 // To make it possible to use colored output with ninja, subprocesses should
219 // be run with a flag that forces them to always print color escape codes.
220 // To make sure these escape codes don't show up in a file if ninja's output
221 // is piped to a file, ninja strips ansi escape codes again if it's not
222 // writing to a |smart_terminal_|.
223 // (Launching subprocesses in pseudo ttys doesn't work because there are
224 // only a few hundred available on some systems, and ninja can launch
225 // thousands of parallel compile commands.)
226 string final_output;
227 if (!printer_.supports_color())
228 final_output = StripAnsiEscapeCodes(output);
229 else
230 final_output = output;
231
232 #ifdef _WIN32
233 // Fix extra CR being added on Windows, writing out CR CR LF (#773)
234 _setmode(_fileno(stdout), _O_BINARY); // Begin Windows extra CR fix
235 #endif
236
237 printer_.PrintOnNewLine(final_output);
238
239 #ifdef _WIN32
240 _setmode(_fileno(stdout), _O_TEXT); // End Windows extra CR fix
241 #endif
242 }
243 }
244
BuildLoadDyndeps()245 void StatusPrinter::BuildLoadDyndeps() {
246 // The DependencyScan calls EXPLAIN() to print lines explaining why
247 // it considers a portion of the graph to be out of date. Normally
248 // this is done before the build starts, but our caller is about to
249 // load a dyndep file during the build. Doing so may generate more
250 // explanation lines (via fprintf directly to stderr), but in an
251 // interactive console the cursor is currently at the end of a status
252 // line. Start a new line so that the first explanation does not
253 // append to the status line. After the explanations are done a
254 // new build status line will appear.
255 if (g_explaining)
256 printer_.PrintOnNewLine("");
257 }
258
BuildStarted()259 void StatusPrinter::BuildStarted() {
260 started_edges_ = 0;
261 finished_edges_ = 0;
262 running_edges_ = 0;
263 }
264
BuildFinished()265 void StatusPrinter::BuildFinished() {
266 printer_.SetConsoleLocked(false);
267 printer_.PrintOnNewLine("");
268 }
269
FormatProgressStatus(const char * progress_status_format,int64_t time_millis) const270 string StatusPrinter::FormatProgressStatus(const char* progress_status_format,
271 int64_t time_millis) const {
272 string out;
273 char buf[32];
274 for (const char* s = progress_status_format; *s != '\0'; ++s) {
275 if (*s == '%') {
276 ++s;
277 switch (*s) {
278 case '%':
279 out.push_back('%');
280 break;
281
282 // Started edges.
283 case 's':
284 snprintf(buf, sizeof(buf), "%d", started_edges_);
285 out += buf;
286 break;
287
288 // Total edges.
289 case 't':
290 snprintf(buf, sizeof(buf), "%d", total_edges_);
291 out += buf;
292 break;
293
294 // Running edges.
295 case 'r': {
296 snprintf(buf, sizeof(buf), "%d", running_edges_);
297 out += buf;
298 break;
299 }
300
301 // Unstarted edges.
302 case 'u':
303 snprintf(buf, sizeof(buf), "%d", total_edges_ - started_edges_);
304 out += buf;
305 break;
306
307 // Finished edges.
308 case 'f':
309 snprintf(buf, sizeof(buf), "%d", finished_edges_);
310 out += buf;
311 break;
312
313 // Overall finished edges per second.
314 case 'o':
315 SnprintfRate(finished_edges_ / (time_millis_ / 1e3), buf, "%.1f");
316 out += buf;
317 break;
318
319 // Current rate, average over the last '-j' jobs.
320 case 'c':
321 current_rate_.UpdateRate(finished_edges_, time_millis_);
322 SnprintfRate(current_rate_.rate(), buf, "%.1f");
323 out += buf;
324 break;
325
326 // Percentage of edges completed
327 case 'p': {
328 int percent = 0;
329 if (finished_edges_ != 0 && total_edges_ != 0)
330 percent = (100 * finished_edges_) / total_edges_;
331 snprintf(buf, sizeof(buf), "%3i%%", percent);
332 out += buf;
333 break;
334 }
335
336 #define FORMAT_TIME_HMMSS(t) \
337 "%" PRId64 ":%02" PRId64 ":%02" PRId64 "", (t) / 3600, ((t) % 3600) / 60, \
338 (t) % 60
339 #define FORMAT_TIME_MMSS(t) "%02" PRId64 ":%02" PRId64 "", (t) / 60, (t) % 60
340
341 // Wall time
342 case 'e': // elapsed, seconds
343 case 'w': // elapsed, human-readable
344 case 'E': // ETA, seconds
345 case 'W': // ETA, human-readable
346 {
347 double elapsed_sec = time_millis_ / 1e3;
348 double eta_sec = -1; // To be printed as "?".
349 if (time_predicted_percentage_ != 0.0) {
350 // So, we know that we've spent time_millis_ wall clock,
351 // and that is time_predicted_percentage_ percent.
352 // How much time will we need to complete 100%?
353 double total_wall_time = time_millis_ / time_predicted_percentage_;
354 // Naturally, that gives us the time remaining.
355 eta_sec = (total_wall_time - time_millis_) / 1e3;
356 }
357
358 const bool print_with_hours =
359 elapsed_sec >= 60 * 60 || eta_sec >= 60 * 60;
360
361 double sec = -1;
362 switch (*s) {
363 case 'e': // elapsed, seconds
364 case 'w': // elapsed, human-readable
365 sec = elapsed_sec;
366 break;
367 case 'E': // ETA, seconds
368 case 'W': // ETA, human-readable
369 sec = eta_sec;
370 break;
371 }
372
373 if (sec < 0)
374 snprintf(buf, sizeof(buf), "?");
375 else {
376 switch (*s) {
377 case 'e': // elapsed, seconds
378 case 'E': // ETA, seconds
379 snprintf(buf, sizeof(buf), "%.3f", sec);
380 break;
381 case 'w': // elapsed, human-readable
382 case 'W': // ETA, human-readable
383 if (print_with_hours)
384 snprintf(buf, sizeof(buf), FORMAT_TIME_HMMSS((int64_t)sec));
385 else
386 snprintf(buf, sizeof(buf), FORMAT_TIME_MMSS((int64_t)sec));
387 break;
388 }
389 }
390 out += buf;
391 break;
392 }
393
394 // Percentage of time spent out of the predicted time total
395 case 'P': {
396 snprintf(buf, sizeof(buf), "%3i%%",
397 (int)(100. * time_predicted_percentage_));
398 out += buf;
399 break;
400 }
401
402 default:
403 Fatal("unknown placeholder '%%%c' in $NINJA_STATUS", *s);
404 return "";
405 }
406 } else {
407 out.push_back(*s);
408 }
409 }
410
411 return out;
412 }
413
PrintStatus(const Edge * edge,int64_t time_millis)414 void StatusPrinter::PrintStatus(const Edge* edge, int64_t time_millis) {
415 if (config_.verbosity == BuildConfig::QUIET
416 || config_.verbosity == BuildConfig::NO_STATUS_UPDATE)
417 return;
418
419 RecalculateProgressPrediction();
420
421 bool force_full_command = config_.verbosity == BuildConfig::VERBOSE;
422
423 string to_print = edge->GetBinding("description");
424 if (to_print.empty() || force_full_command)
425 to_print = edge->GetBinding("command");
426
427 to_print = FormatProgressStatus(progress_status_format_, time_millis)
428 + to_print;
429
430 printer_.Print(to_print,force_full_command ? LinePrinter::FULL : LinePrinter::ELIDE);
431 }
432
Warning(const char * msg,...)433 void StatusPrinter::Warning(const char* msg, ...) {
434 va_list ap;
435 va_start(ap, msg);
436 ::Warning(msg, ap);
437 va_end(ap);
438 }
439
Error(const char * msg,...)440 void StatusPrinter::Error(const char* msg, ...) {
441 va_list ap;
442 va_start(ap, msg);
443 ::Error(msg, ap);
444 va_end(ap);
445 }
446
Info(const char * msg,...)447 void StatusPrinter::Info(const char* msg, ...) {
448 va_list ap;
449 va_start(ap, msg);
450 ::Info(msg, ap);
451 va_end(ap);
452 }
453