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