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 
38 using namespace std;
39 
41  return new StatusPrinter(config);
42 }
43 
45  : config_(config), started_edges_(0), finished_edges_(0), total_edges_(0),
46  running_edges_(0), progress_status_format_(NULL),
47  current_rate_(config.parallelism) {
48  // Don't do anything fancy in verbose mode.
51 
52  progress_status_format_ = getenv("NINJA_STATUS");
54  progress_status_format_ = "[%f/%t] ";
55 }
56 
58  ++total_edges_;
59 
60  // Do we know how long did this edge take last time?
61  if (edge->prev_elapsed_time_millis != -1) {
67  } else
69 }
70 
72  --total_edges_;
73 
74  // Do we know how long did this edge take last time?
75  if (edge->prev_elapsed_time_millis != -1) {
81  } else
83 }
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())
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 
177 void 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;
181  ++finished_edges_;
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 
198  return;
199 
200  if (!edge->use_console())
201  PrintStatus(edge, end_time_millis);
202 
203  --running_edges_;
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 
254  started_edges_ = 0;
255  finished_edges_ = 0;
256  running_edges_ = 0;
257 }
258 
260  printer_.SetConsoleLocked(false);
262 }
263 
264 string 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 
408 void 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.
419  for (const auto& exp : explanations) {
420  fprintf(stderr, "ninja explain: %s\n", exp.c_str());
421  }
422  }
423  }
424 
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 
444 void StatusPrinter::Warning(const char* msg, ...) {
445  va_list ap;
446  va_start(ap, msg);
447  ::Warning(msg, ap);
448  va_end(ap);
449 }
450 
451 void StatusPrinter::Error(const char* msg, ...) {
452  va_list ap;
453  va_start(ap, msg);
454  ::Error(msg, ap);
455  va_end(ap);
456 }
457 
458 void 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
@ VERBOSE
Definition: build.h:183
@ NORMAL
Definition: build.h:182
@ NO_STATUS_UPDATE
Definition: build.h:181
Verbosity verbosity
Definition: build.h:185
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
void LookupAndAppend(const void *item, std::vector< std::string > *out)
Lookup the explanations recorded for |item|, and append them to |*out|, if any.
Definition: explanations.h:46
void set_smart_terminal(bool smart)
Definition: line_printer.h:27
void Print(std::string to_print, LineType type)
Overprints the current line.
Definition: line_printer.cc:67
void SetConsoleLocked(bool locked)
Lock or unlock the console.
bool supports_color() const
Definition: line_printer.h:29
bool is_smart_terminal() const
Definition: line_printer.h:26
void PrintOnNewLine(const std::string &to_print)
Prints a string on a new line, not overprinting previous output.
Information about a node in the dependency graph: the file, whether it's dirty, mtime,...
Definition: graph.h:42
void UpdateRate(int update_hint, int64_t time_millis)
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