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