Ninja
build_log.cc
Go to the documentation of this file.
1 // Copyright 2011 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 // On AIX, inttypes.h gets indirectly included by build_log.h.
16 // It's easiest just to ask for the printf format macros right away.
17 #ifndef _WIN32
18 #ifndef __STDC_FORMAT_MACROS
19 #define __STDC_FORMAT_MACROS
20 #endif
21 #endif
22 
23 #include "build_log.h"
24 
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #ifndef _WIN32
30 #include <inttypes.h>
31 #include <unistd.h>
32 #endif
33 
34 #include "build.h"
35 #include "graph.h"
36 #include "metrics.h"
37 #include "util.h"
38 #if defined(_MSC_VER) && (_MSC_VER < 1800)
39 #define strtoll _strtoi64
40 #endif
41 
42 // Implementation details:
43 // Each run's log appends to the log file.
44 // To load, we run through all log entries in series, throwing away
45 // older runs.
46 // Once the number of redundant entries exceeds a threshold, we write
47 // out a new file and replace the existing one with it.
48 
49 namespace {
50 
51 const char kFileSignature[] = "# ninja log v%d\n";
52 const int kOldestSupportedVersion = 4;
53 const int kCurrentVersion = 5;
54 
55 // 64bit MurmurHash2, by Austin Appleby
56 #if defined(_MSC_VER)
57 #define BIG_CONSTANT(x) (x)
58 #else // defined(_MSC_VER)
59 #define BIG_CONSTANT(x) (x##LLU)
60 #endif // !defined(_MSC_VER)
61 inline
62 uint64_t MurmurHash64A(const void* key, size_t len) {
63  static const uint64_t seed = 0xDECAFBADDECAFBADull;
64  const uint64_t m = BIG_CONSTANT(0xc6a4a7935bd1e995);
65  const int r = 47;
66  uint64_t h = seed ^ (len * m);
67  const unsigned char* data = (const unsigned char*)key;
68  while (len >= 8) {
69  uint64_t k;
70  memcpy(&k, data, sizeof k);
71  k *= m;
72  k ^= k >> r;
73  k *= m;
74  h ^= k;
75  h *= m;
76  data += 8;
77  len -= 8;
78  }
79  switch (len & 7)
80  {
81  case 7: h ^= uint64_t(data[6]) << 48;
83  case 6: h ^= uint64_t(data[5]) << 40;
85  case 5: h ^= uint64_t(data[4]) << 32;
87  case 4: h ^= uint64_t(data[3]) << 24;
89  case 3: h ^= uint64_t(data[2]) << 16;
91  case 2: h ^= uint64_t(data[1]) << 8;
93  case 1: h ^= uint64_t(data[0]);
94  h *= m;
95  };
96  h ^= h >> r;
97  h *= m;
98  h ^= h >> r;
99  return h;
100 }
101 #undef BIG_CONSTANT
102 
103 
104 } // namespace
105 
106 // static
108  return MurmurHash64A(command.str_, command.len_);
109 }
110 
111 BuildLog::LogEntry::LogEntry(const string& output)
112  : output(output) {}
113 
114 BuildLog::LogEntry::LogEntry(const string& output, uint64_t command_hash,
115  int start_time, int end_time, TimeStamp restat_mtime)
116  : output(output), command_hash(command_hash),
117  start_time(start_time), end_time(end_time), mtime(restat_mtime)
118 {}
119 
121  : log_file_(NULL), needs_recompaction_(false) {}
122 
124  Close();
125 }
126 
127 bool BuildLog::OpenForWrite(const string& path, const BuildLogUser& user,
128  string* err) {
129  if (needs_recompaction_) {
130  if (!Recompact(path, user, err))
131  return false;
132  }
133 
134  log_file_ = fopen(path.c_str(), "ab");
135  if (!log_file_) {
136  *err = strerror(errno);
137  return false;
138  }
139  setvbuf(log_file_, NULL, _IOLBF, BUFSIZ);
140  SetCloseOnExec(fileno(log_file_));
141 
142  // Opening a file in append mode doesn't set the file pointer to the file's
143  // end on Windows. Do that explicitly.
144  fseek(log_file_, 0, SEEK_END);
145 
146  if (ftell(log_file_) == 0) {
147  if (fprintf(log_file_, kFileSignature, kCurrentVersion) < 0) {
148  *err = strerror(errno);
149  return false;
150  }
151  }
152 
153  return true;
154 }
155 
156 bool BuildLog::RecordCommand(Edge* edge, int start_time, int end_time,
157  TimeStamp mtime) {
158  string command = edge->EvaluateCommand(true);
159  uint64_t command_hash = LogEntry::HashCommand(command);
160  for (vector<Node*>::iterator out = edge->outputs_.begin();
161  out != edge->outputs_.end(); ++out) {
162  const string& path = (*out)->path();
163  Entries::iterator i = entries_.find(path);
164  LogEntry* log_entry;
165  if (i != entries_.end()) {
166  log_entry = i->second;
167  } else {
168  log_entry = new LogEntry(path);
169  entries_.insert(Entries::value_type(log_entry->output, log_entry));
170  }
171  log_entry->command_hash = command_hash;
172  log_entry->start_time = start_time;
173  log_entry->end_time = end_time;
174  log_entry->mtime = mtime;
175 
176  if (log_file_) {
177  if (!WriteEntry(log_file_, *log_entry))
178  return false;
179  if (fflush(log_file_) != 0) {
180  return false;
181  }
182  }
183  }
184  return true;
185 }
186 
188  if (log_file_)
189  fclose(log_file_);
190  log_file_ = NULL;
191 }
192 
193 struct LineReader {
194  explicit LineReader(FILE* file)
195  : file_(file), buf_end_(buf_), line_start_(buf_), line_end_(NULL) {
196  memset(buf_, 0, sizeof(buf_));
197  }
198 
199  // Reads a \n-terminated line from the file passed to the constructor.
200  // On return, *line_start points to the beginning of the next line, and
201  // *line_end points to the \n at the end of the line. If no newline is seen
202  // in a fixed buffer size, *line_end is set to NULL. Returns false on EOF.
203  bool ReadLine(char** line_start, char** line_end) {
204  if (line_start_ >= buf_end_ || !line_end_) {
205  // Buffer empty, refill.
206  size_t size_read = fread(buf_, 1, sizeof(buf_), file_);
207  if (!size_read)
208  return false;
209  line_start_ = buf_;
210  buf_end_ = buf_ + size_read;
211  } else {
212  // Advance to next line in buffer.
213  line_start_ = line_end_ + 1;
214  }
215 
216  line_end_ = (char*)memchr(line_start_, '\n', buf_end_ - line_start_);
217  if (!line_end_) {
218  // No newline. Move rest of data to start of buffer, fill rest.
219  size_t already_consumed = line_start_ - buf_;
220  size_t size_rest = (buf_end_ - buf_) - already_consumed;
221  memmove(buf_, line_start_, size_rest);
222 
223  size_t read = fread(buf_ + size_rest, 1, sizeof(buf_) - size_rest, file_);
224  buf_end_ = buf_ + size_rest + read;
225  line_start_ = buf_;
226  line_end_ = (char*)memchr(line_start_, '\n', buf_end_ - line_start_);
227  }
228 
229  *line_start = line_start_;
230  *line_end = line_end_;
231  return true;
232  }
233 
234  private:
235  FILE* file_;
236  char buf_[256 << 10];
237  char* buf_end_; // Points one past the last valid byte in |buf_|.
238 
239  char* line_start_;
240  // Points at the next \n in buf_ after line_start, or NULL.
241  char* line_end_;
242 };
243 
244 bool BuildLog::Load(const string& path, string* err) {
245  METRIC_RECORD(".ninja_log load");
246  FILE* file = fopen(path.c_str(), "r");
247  if (!file) {
248  if (errno == ENOENT)
249  return true;
250  *err = strerror(errno);
251  return false;
252  }
253 
254  int log_version = 0;
255  int unique_entry_count = 0;
256  int total_entry_count = 0;
257 
258  LineReader reader(file);
259  char* line_start = 0;
260  char* line_end = 0;
261  while (reader.ReadLine(&line_start, &line_end)) {
262  if (!log_version) {
263  sscanf(line_start, kFileSignature, &log_version);
264 
265  if (log_version < kOldestSupportedVersion) {
266  *err = ("build log version invalid, perhaps due to being too old; "
267  "starting over");
268  fclose(file);
269  unlink(path.c_str());
270  // Don't report this as a failure. An empty build log will cause
271  // us to rebuild the outputs anyway.
272  return true;
273  }
274  }
275 
276  // If no newline was found in this chunk, read the next.
277  if (!line_end)
278  continue;
279 
280  const char kFieldSeparator = '\t';
281 
282  char* start = line_start;
283  char* end = (char*)memchr(start, kFieldSeparator, line_end - start);
284  if (!end)
285  continue;
286  *end = 0;
287 
288  int start_time = 0, end_time = 0;
289  TimeStamp restat_mtime = 0;
290 
291  start_time = atoi(start);
292  start = end + 1;
293 
294  end = (char*)memchr(start, kFieldSeparator, line_end - start);
295  if (!end)
296  continue;
297  *end = 0;
298  end_time = atoi(start);
299  start = end + 1;
300 
301  end = (char*)memchr(start, kFieldSeparator, line_end - start);
302  if (!end)
303  continue;
304  *end = 0;
305  restat_mtime = strtoll(start, NULL, 10);
306  start = end + 1;
307 
308  end = (char*)memchr(start, kFieldSeparator, line_end - start);
309  if (!end)
310  continue;
311  string output = string(start, end - start);
312 
313  start = end + 1;
314  end = line_end;
315 
316  LogEntry* entry;
317  Entries::iterator i = entries_.find(output);
318  if (i != entries_.end()) {
319  entry = i->second;
320  } else {
321  entry = new LogEntry(output);
322  entries_.insert(Entries::value_type(entry->output, entry));
323  ++unique_entry_count;
324  }
325  ++total_entry_count;
326 
327  entry->start_time = start_time;
328  entry->end_time = end_time;
329  entry->mtime = restat_mtime;
330  if (log_version >= 5) {
331  char c = *end; *end = '\0';
332  entry->command_hash = (uint64_t)strtoull(start, NULL, 16);
333  *end = c;
334  } else {
336  end - start));
337  }
338  }
339  fclose(file);
340 
341  if (!line_start) {
342  return true; // file was empty
343  }
344 
345  // Decide whether it's time to rebuild the log:
346  // - if we're upgrading versions
347  // - if it's getting large
348  int kMinCompactionEntryCount = 100;
349  int kCompactionRatio = 3;
350  if (log_version < kCurrentVersion) {
351  needs_recompaction_ = true;
352  } else if (total_entry_count > kMinCompactionEntryCount &&
353  total_entry_count > unique_entry_count * kCompactionRatio) {
354  needs_recompaction_ = true;
355  }
356 
357  return true;
358 }
359 
361  Entries::iterator i = entries_.find(path);
362  if (i != entries_.end())
363  return i->second;
364  return NULL;
365 }
366 
367 bool BuildLog::WriteEntry(FILE* f, const LogEntry& entry) {
368  return fprintf(f, "%d\t%d\t%" PRId64 "\t%s\t%" PRIx64 "\n",
369  entry.start_time, entry.end_time, entry.mtime,
370  entry.output.c_str(), entry.command_hash) > 0;
371 }
372 
373 bool BuildLog::Recompact(const string& path, const BuildLogUser& user,
374  string* err) {
375  METRIC_RECORD(".ninja_log recompact");
376 
377  Close();
378  string temp_path = path + ".recompact";
379  FILE* f = fopen(temp_path.c_str(), "wb");
380  if (!f) {
381  *err = strerror(errno);
382  return false;
383  }
384 
385  if (fprintf(f, kFileSignature, kCurrentVersion) < 0) {
386  *err = strerror(errno);
387  fclose(f);
388  return false;
389  }
390 
391  vector<StringPiece> dead_outputs;
392  for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
393  if (user.IsPathDead(i->first)) {
394  dead_outputs.push_back(i->first);
395  continue;
396  }
397 
398  if (!WriteEntry(f, *i->second)) {
399  *err = strerror(errno);
400  fclose(f);
401  return false;
402  }
403  }
404 
405  for (size_t i = 0; i < dead_outputs.size(); ++i)
406  entries_.erase(dead_outputs[i]);
407 
408  fclose(f);
409  if (unlink(path.c_str()) < 0) {
410  *err = strerror(errno);
411  return false;
412  }
413 
414  if (rename(temp_path.c_str(), path.c_str()) < 0) {
415  *err = strerror(errno);
416  return false;
417  }
418 
419  return true;
420 }
const int kCurrentVersion
Definition: deps_log.cc:36
bool needs_recompaction_
Definition: build_log.h:90
const char * str_
Definition: string_piece.h:67
const char kFileSignature[]
Definition: deps_log.cc:35
bool RecordCommand(Edge *edge, int start_time, int end_time, TimeStamp mtime=0)
Definition: build_log.cc:156
char buf_[256<< 10]
Definition: build_log.cc:236
bool Recompact(const string &path, const BuildLogUser &user, string *err)
Rewrite the known log entries, throwing away old data.
Definition: build_log.cc:373
StringPiece represents a slice of a string whose memory is managed externally.
Definition: string_piece.h:27
bool WriteEntry(FILE *f, const LogEntry &entry)
Serialize an entry into a log file.
Definition: build_log.cc:367
#define PRId64
Definition: win32port.h:33
void Close()
Definition: build_log.cc:187
An edge in the dependency graph; links between Nodes using Rules.
Definition: graph.h:131
string EvaluateCommand(bool incl_rsp_file=false)
Expand all variables in a command and return it as a string.
Definition: graph.cc:362
virtual bool IsPathDead(StringPiece s) const =0
Return if a given output is no longer part of the build manifest.
void SetCloseOnExec(int fd)
Mark a file descriptor to not be inherited on exec()s.
Definition: util.cc:375
uint64_t command_hash
Definition: build_log.h:56
bool OpenForWrite(const string &path, const BuildLogUser &user, string *err)
Definition: build_log.cc:127
FILE * file_
Definition: build_log.cc:235
#define PRIx64
Definition: win32port.h:35
FILE * log_file_
Definition: build_log.h:89
#define BIG_CONSTANT(x)
Definition: build_log.cc:59
char * buf_end_
Definition: build_log.cc:237
#define NINJA_FALLTHROUGH
Definition: util.h:48
int64_t TimeStamp
Definition: timestamp.h:31
LogEntry(const string &output)
Definition: build_log.cc:111
#define METRIC_RECORD(name)
The primary interface to metrics.
Definition: metrics.h:85
char * line_start_
Definition: build_log.cc:239
TimeStamp mtime
Definition: build_log.h:59
static uint64_t HashCommand(StringPiece command)
Definition: build_log.cc:107
LogEntry * LookupByOutput(const string &path)
Lookup a previously-run command by its output path.
Definition: build_log.cc:360
size_t len_
Definition: string_piece.h:68
unsigned long long uint64_t
Definition: win32port.h:29
char * line_end_
Definition: build_log.cc:241
bool Load(const string &path, string *err)
Load the on-disk log.
Definition: build_log.cc:244
Entries entries_
Definition: build_log.h:88
bool ReadLine(char **line_start, char **line_end)
Definition: build_log.cc:203
Can answer questions about the manifest for the BuildLog.
Definition: build_log.h:29
LineReader(FILE *file)
Definition: build_log.cc:194
vector< Node * > outputs_
Definition: graph.h:164