Ninja
status_printer.cc
Go to the documentation of this file.
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_printer.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 "build.h"
35#include "debug_flags.h"
36#include "exit_status.h"
37
38using namespace std;
39
41 return new StatusPrinter(config);
42}
43
47 current_rate_(config.parallelism) {
48 // Don't do anything fancy in verbose mode.
49 if (config_.verbosity != BuildConfig::NORMAL)
50 printer_.set_smart_terminal(false);
51
52 progress_status_format_ = getenv("NINJA_STATUS");
54 progress_status_format_ = "[%f/%t] ";
55}
56
70
84
86 int64_t start_time_millis) {
89 time_millis_ = start_time_millis;
90
91 if (edge->use_console() || printer_.is_smart_terminal())
92 PrintStatus(edge, start_time_millis);
93
94 if (edge->use_console())
95 printer_.SetConsoleLocked(true);
96}
97
100
101 // Sometimes, the previous and actual times may be wildly different.
102 // For example, the previous build may have been fully recovered from ccache,
103 // so it was blazing fast, while the new build no longer gets hits from ccache
104 // for whatever reason, so it actually compiles code, which takes much longer.
105 // We should detect such cases, and avoid using "wrong" previous times.
106
107 // Note that we will only use the previous times if there are edges with
108 // previous time knowledge remaining.
109 bool use_previous_times = eta_predictable_edges_remaining_ &&
111
112 // Iff we have sufficient statistical information for the current run,
113 // that is, if we have took at least 15 sec AND finished at least 5% of edges,
114 // we can check whether our performance so far matches the previous one.
115 if (use_previous_times && total_edges_ && finished_edges_ &&
116 (time_millis_ >= 15 * 1e3) &&
117 (((double)finished_edges_ / total_edges_) >= 0.05)) {
118 // Over the edges we've just run, how long did they take on average?
119 double actual_average_cpu_time_millis =
121 // What is the previous average, for the edges with such knowledge?
122 double previous_average_cpu_time_millis =
125
126 double ratio = std::max(previous_average_cpu_time_millis,
127 actual_average_cpu_time_millis) /
128 std::min(previous_average_cpu_time_millis,
129 actual_average_cpu_time_millis);
130
131 // Let's say that the average times should differ by less than 10x
132 use_previous_times = ratio < 10;
133 }
134
135 int edges_with_known_runtime = finished_edges_;
136 if (use_previous_times)
137 edges_with_known_runtime += eta_predictable_edges_remaining_;
138 if (edges_with_known_runtime == 0)
139 return;
140
141 int edges_with_unknown_runtime = use_previous_times
144
145 // Given the time elapsed on the edges we've just run,
146 // and the runtime of the edges for which we know previous runtime,
147 // what's the edge's average runtime?
148 int64_t edges_known_runtime_total_millis = cpu_time_millis_;
149 if (use_previous_times)
150 edges_known_runtime_total_millis +=
152
153 double average_cpu_time_millis =
154 (double)edges_known_runtime_total_millis / edges_with_known_runtime;
155
156 // For the edges for which we do not have the previous runtime,
157 // let's assume that their average runtime is the same as for the other edges,
158 // and we therefore can predict their remaining runtime.
159 double unpredictable_cpu_time_remaining_millis =
160 average_cpu_time_millis * edges_with_unknown_runtime;
161
162 // And therefore we can predict the remaining and total runtimes.
163 double total_cpu_time_remaining_millis =
164 unpredictable_cpu_time_remaining_millis;
165 if (use_previous_times)
166 total_cpu_time_remaining_millis +=
168 double total_cpu_time_millis =
169 cpu_time_millis_ + total_cpu_time_remaining_millis;
170 if (total_cpu_time_millis == 0.0)
171 return;
172
173 // After that we can tell how much work we've completed, in time units.
174 time_predicted_percentage_ = cpu_time_millis_ / total_cpu_time_millis;
175}
176
177void StatusPrinter::BuildEdgeFinished(Edge* edge, int64_t start_time_millis,
178 int64_t end_time_millis, ExitStatus exit_code,
179 const string& output) {
180 time_millis_ = end_time_millis;
182
183 int64_t elapsed = end_time_millis - start_time_millis;
184 cpu_time_millis_ += elapsed;
185
186 // Do we know how long did this edge take last time?
187 if (edge->prev_elapsed_time_millis != -1) {
191 } else
193
194 if (edge->use_console())
195 printer_.SetConsoleLocked(false);
196
197 if (config_.verbosity == BuildConfig::QUIET)
198 return;
199
200 if (!edge->use_console())
201 PrintStatus(edge, end_time_millis);
202
204
205 // Print the command that is spewing before printing its output.
206 if (exit_code != ExitSuccess) {
207 string outputs;
208 for (vector<Node*>::const_iterator o = edge->outputs_.begin();
209 o != edge->outputs_.end(); ++o)
210 outputs += (*o)->path() + " ";
211
212 string failed = "FAILED: [code=" + std::to_string(exit_code) + "] ";
213 if (printer_.supports_color()) {
214 printer_.PrintOnNewLine("\x1B[31m" + failed + "\x1B[0m" + outputs + "\n");
215 } else {
216 printer_.PrintOnNewLine(failed + outputs + "\n");
217 }
218 printer_.PrintOnNewLine(edge->EvaluateCommand() + "\n");
219 }
220
221 if (!output.empty()) {
222#ifdef _WIN32
223 // Fix extra CR being added on Windows, writing out CR CR LF (#773)
224 fflush(stdout); // Begin Windows extra CR fix
225 _setmode(_fileno(stdout), _O_BINARY);
226#endif
227
228 // ninja sets stdout and stderr of subprocesses to a pipe, to be able to
229 // check if the output is empty. Some compilers, e.g. clang, check
230 // isatty(stderr) to decide if they should print colored output.
231 // To make it possible to use colored output with ninja, subprocesses should
232 // be run with a flag that forces them to always print color escape codes.
233 // To make sure these escape codes don't show up in a file if ninja's output
234 // is piped to a file, ninja strips ansi escape codes again if it's not
235 // writing to a |smart_terminal_|.
236 // (Launching subprocesses in pseudo ttys doesn't work because there are
237 // only a few hundred available on some systems, and ninja can launch
238 // thousands of parallel compile commands.)
239 if (printer_.supports_color() || output.find('\x1b') == std::string::npos) {
240 printer_.PrintOnNewLine(output);
241 } else {
242 std::string final_output = StripAnsiEscapeCodes(output);
243 printer_.PrintOnNewLine(final_output);
244 }
245
246#ifdef _WIN32
247 fflush(stdout);
248 _setmode(_fileno(stdout), _O_TEXT); // End Windows extra CR fix
249#endif
250 }
251}
252
258
260 printer_.SetConsoleLocked(false);
261 printer_.PrintOnNewLine("");
262}
263
264string StatusPrinter::FormatProgressStatus(const char* progress_status_format,
265 int64_t time_millis) const {
266 string out;
267 char buf[32];
268 for (const char* s = progress_status_format; *s != '\0'; ++s) {
269 if (*s == '%') {
270 ++s;
271 switch (*s) {
272 case '%':
273 out.push_back('%');
274 break;
275
276 // Started edges.
277 case 's':
278 snprintf(buf, sizeof(buf), "%d", started_edges_);
279 out += buf;
280 break;
281
282 // Total edges.
283 case 't':
284 snprintf(buf, sizeof(buf), "%d", total_edges_);
285 out += buf;
286 break;
287
288 // Running edges.
289 case 'r': {
290 snprintf(buf, sizeof(buf), "%d", running_edges_);
291 out += buf;
292 break;
293 }
294
295 // Unstarted edges.
296 case 'u':
297 snprintf(buf, sizeof(buf), "%d", total_edges_ - started_edges_);
298 out += buf;
299 break;
300
301 // Finished edges.
302 case 'f':
303 snprintf(buf, sizeof(buf), "%d", finished_edges_);
304 out += buf;
305 break;
306
307 // Overall finished edges per second.
308 case 'o':
309 SnprintfRate(finished_edges_ / (time_millis_ / 1e3), buf, "%.1f");
310 out += buf;
311 break;
312
313 // Current rate, average over the last '-j' jobs.
314 case 'c':
316 SnprintfRate(current_rate_.rate(), buf, "%.1f");
317 out += buf;
318 break;
319
320 // Percentage of edges completed
321 case 'p': {
322 int percent = 0;
323 if (finished_edges_ != 0 && total_edges_ != 0)
324 percent = (100 * finished_edges_) / total_edges_;
325 snprintf(buf, sizeof(buf), "%3i%%", percent);
326 out += buf;
327 break;
328 }
329
330#define FORMAT_TIME_HMMSS(t) \
331 "%" PRId64 ":%02" PRId64 ":%02" PRId64 "", (t) / 3600, ((t) % 3600) / 60, \
332 (t) % 60
333#define FORMAT_TIME_MMSS(t) "%02" PRId64 ":%02" PRId64 "", (t) / 60, (t) % 60
334
335 // Wall time
336 case 'e': // elapsed, seconds
337 case 'w': // elapsed, human-readable
338 case 'E': // ETA, seconds
339 case 'W': // ETA, human-readable
340 {
341 double elapsed_sec = time_millis_ / 1e3;
342 double eta_sec = -1; // To be printed as "?".
343 if (time_predicted_percentage_ != 0.0) {
344 // So, we know that we've spent time_millis_ wall clock,
345 // and that is time_predicted_percentage_ percent.
346 // How much time will we need to complete 100%?
347 double total_wall_time = time_millis_ / time_predicted_percentage_;
348 // Naturally, that gives us the time remaining.
349 eta_sec = (total_wall_time - time_millis_) / 1e3;
350 }
351
352 const bool print_with_hours =
353 elapsed_sec >= 60 * 60 || eta_sec >= 60 * 60;
354
355 double sec = -1;
356 switch (*s) {
357 case 'e': // elapsed, seconds
358 case 'w': // elapsed, human-readable
359 sec = elapsed_sec;
360 break;
361 case 'E': // ETA, seconds
362 case 'W': // ETA, human-readable
363 sec = eta_sec;
364 break;
365 }
366
367 if (sec < 0)
368 snprintf(buf, sizeof(buf), "?");
369 else {
370 switch (*s) {
371 case 'e': // elapsed, seconds
372 case 'E': // ETA, seconds
373 snprintf(buf, sizeof(buf), "%.3f", sec);
374 break;
375 case 'w': // elapsed, human-readable
376 case 'W': // ETA, human-readable
377 if (print_with_hours)
378 snprintf(buf, sizeof(buf), FORMAT_TIME_HMMSS((int64_t)sec));
379 else
380 snprintf(buf, sizeof(buf), FORMAT_TIME_MMSS((int64_t)sec));
381 break;
382 }
383 }
384 out += buf;
385 break;
386 }
387
388 // Percentage of time spent out of the predicted time total
389 case 'P': {
390 snprintf(buf, sizeof(buf), "%3i%%",
391 (int)(100. * time_predicted_percentage_));
392 out += buf;
393 break;
394 }
395
396 default:
397 Fatal("unknown placeholder '%%%c' in $NINJA_STATUS", *s);
398 return "";
399 }
400 } else {
401 out.push_back(*s);
402 }
403 }
404
405 return out;
406}
407
408void StatusPrinter::PrintStatus(const Edge* edge, int64_t time_millis) {
409 if (explanations_) {
410 // Collect all explanations for the current edge's outputs.
411 std::vector<std::string> explanations;
412 for (Node* output : edge->outputs_) {
413 explanations_->LookupAndAppend(output, &explanations);
414 }
415 if (!explanations.empty()) {
416 // Start a new line so that the first explanation does not append to the
417 // status line.
418 printer_.PrintOnNewLine("");
419 for (const auto& exp : explanations) {
420 fprintf(stderr, "ninja explain: %s\n", exp.c_str());
421 }
422 }
423 }
424
425 if (config_.verbosity == BuildConfig::QUIET
427 return;
428
430
431 bool force_full_command = config_.verbosity == BuildConfig::VERBOSE;
432
433 string to_print = edge->GetBinding("description");
434 if (to_print.empty() || force_full_command)
435 to_print = edge->GetBinding("command");
436
437 to_print = FormatProgressStatus(progress_status_format_, time_millis)
438 + to_print;
439
440 printer_.Print(to_print,
441 force_full_command ? LinePrinter::FULL : LinePrinter::ELIDE);
442}
443
444void StatusPrinter::Warning(const char* msg, ...) {
445 va_list ap;
446 va_start(ap, msg);
447 ::Warning(msg, ap);
448 va_end(ap);
449}
450
451void StatusPrinter::Error(const char* msg, ...) {
452 va_list ap;
453 va_start(ap, msg);
454 ::Error(msg, ap);
455 va_end(ap);
456}
457
458void StatusPrinter::Info(const char* msg, ...) {
459 va_list ap;
460 va_start(ap, msg);
461 ::Info(msg, ap);
462 va_end(ap);
463}
ExitStatus
Definition exit_status.h:27
@ ExitSuccess
Definition exit_status.h:28
Definition hash_map.h:26
#define FORMAT_TIME_HMMSS(t)
#define FORMAT_TIME_MMSS(t)
Options (e.g. verbosity, parallelism) passed to a build.
Definition build.h:176
@ NO_STATUS_UPDATE
Definition build.h:181
An edge in the dependency graph; links between Nodes using Rules.
Definition graph.h:175
int64_t prev_elapsed_time_millis
Definition graph.h:272
std::string GetBinding(const std::string &key) const
Returns the shell-escaped value of |key|.
Definition graph.cc:511
std::vector< Node * > outputs_
Definition graph.h:217
std::string EvaluateCommand(bool incl_rsp_file=false) const
Expand all variables in a command and return it as a string.
Definition graph.cc:501
bool use_console() const
Definition graph.cc:567
Information about a node in the dependency graph: the file, whether it's dirty, mtime,...
Definition graph.h:42
Implementation of the Status interface that prints the status as human-readable strings to stdout.
void PrintStatus(const Edge *edge, int64_t time_millis)
void BuildEdgeStarted(const Edge *edge, int64_t start_time_millis) override
int64_t cpu_time_millis_
How much cpu clock elapsed so far?
void Warning(const char *msg,...) override
void BuildFinished() override
SlidingRateInfo current_rate_
LinePrinter printer_
Prints progress output.
void Error(const char *msg,...) override
void EdgeAddedToPlan(const Edge *edge) override
Callbacks for the Plan to notify us about adding/removing Edge's.
void EdgeRemovedFromPlan(const Edge *edge) override
int64_t time_millis_
How much wall clock elapsed so far?
const char * progress_status_format_
The custom progress status format to use.
void BuildEdgeFinished(Edge *edge, int64_t start_time_millis, int64_t end_time_millis, ExitStatus exit_code, const std::string &output) override
void SnprintfRate(double rate, char(&buf)[S], const char *format) const
int eta_predictable_edges_total_
Out of all the edges, for how many do we know previous time?
int eta_unpredictable_edges_remaining_
For how many edges we don't know the previous run time?
void Info(const char *msg,...) override
int64_t eta_predictable_cpu_time_total_millis_
And how much time did they all take?
void RecalculateProgressPrediction()
StatusPrinter(const BuildConfig &config)
int eta_predictable_edges_remaining_
Out of all the non-finished edges, for how many do we know previous time?
void BuildStarted() override
const BuildConfig & config_
int64_t eta_predictable_cpu_time_remaining_millis_
And how much time will they all take?
std::string FormatProgressStatus(const char *progress_status_format, int64_t time_millis) const
Format the progress status string by replacing the placeholders.
double time_predicted_percentage_
What percentage of predicted total time have elapsed already?
Explanations * explanations_
An optional Explanations pointer, used to implement -d explain.
Abstract interface to object that tracks the status of a build: completion fraction,...
Definition status.h:27
static Status * factory(const BuildConfig &)
creates the actual implementation
string StripAnsiEscapeCodes(const string &in)
Definition util.cc:571
void Fatal(const char *msg,...)
Log a fatal message and exit.
Definition util.cc:67
signed long long int64_t
A 64-bit integer type.
Definition win32port.h:28