1 // Copyright 2011 Google Inc. All Rights Reserved.
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
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.
18 #ifndef __STDC_FORMAT_MACROS
19 #define __STDC_FORMAT_MACROS
23 #include "build_log.h"
24 #include "disk_interface.h"
40 #if defined(_MSC_VER) && (_MSC_VER < 1800)
41 #define strtoll _strtoi64
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
48 // Once the number of redundant entries exceeds a threshold, we write
49 // out a new file and replace the existing one with it.
53 const char kFileSignature[] = "# ninja log v%d\n";
54 const int kOldestSupportedVersion = 4;
55 const int kCurrentVersion = 5;
57 // 64bit MurmurHash2, by Austin Appleby
59 #define BIG_CONSTANT(x) (x)
60 #else // defined(_MSC_VER)
61 #define BIG_CONSTANT(x) (x##LLU)
62 #endif // !defined(_MSC_VER)
64 uint64_t MurmurHash64A(const void* key, size_t len) {
65 static const uint64_t seed = 0xDECAFBADDECAFBADull;
66 const uint64_t m = BIG_CONSTANT(0xc6a4a7935bd1e995);
68 uint64_t h = seed ^ (len * m);
69 const unsigned char* data = (const unsigned char*)key;
72 memcpy(&k, data, sizeof k);
83 case 7: h ^= uint64_t(data[6]) << 48;
85 case 6: h ^= uint64_t(data[5]) << 40;
87 case 5: h ^= uint64_t(data[4]) << 32;
89 case 4: h ^= uint64_t(data[3]) << 24;
91 case 3: h ^= uint64_t(data[2]) << 16;
93 case 2: h ^= uint64_t(data[1]) << 8;
95 case 1: h ^= uint64_t(data[0]);
109 uint64_t BuildLog::LogEntry::HashCommand(StringPiece command) {
110 return MurmurHash64A(command.str_, command.len_);
113 BuildLog::LogEntry::LogEntry(const string& output)
116 BuildLog::LogEntry::LogEntry(const string& output, uint64_t command_hash,
117 int start_time, int end_time, TimeStamp restat_mtime)
118 : output(output), command_hash(command_hash),
119 start_time(start_time), end_time(end_time), mtime(restat_mtime)
123 : log_file_(NULL), needs_recompaction_(false) {}
125 BuildLog::~BuildLog() {
129 bool BuildLog::OpenForWrite(const string& path, const BuildLogUser& user,
131 if (needs_recompaction_) {
132 if (!Recompact(path, user, err))
137 log_file_path_ = path; // we don't actually open the file right now, but will
138 // do so on the first write attempt
142 bool BuildLog::RecordCommand(Edge* edge, int start_time, int end_time,
144 string command = edge->EvaluateCommand(true);
145 uint64_t command_hash = LogEntry::HashCommand(command);
146 for (vector<Node*>::iterator out = edge->outputs_.begin();
147 out != edge->outputs_.end(); ++out) {
148 const string& path = (*out)->path();
149 Entries::iterator i = entries_.find(path);
151 if (i != entries_.end()) {
152 log_entry = i->second;
154 log_entry = new LogEntry(path);
155 entries_.insert(Entries::value_type(log_entry->output, log_entry));
157 log_entry->command_hash = command_hash;
158 log_entry->start_time = start_time;
159 log_entry->end_time = end_time;
160 log_entry->mtime = mtime;
162 if (!OpenForWriteIfNeeded()) {
166 if (!WriteEntry(log_file_, *log_entry))
168 if (fflush(log_file_) != 0) {
176 void BuildLog::Close() {
177 OpenForWriteIfNeeded(); // create the file even if nothing has been recorded
183 bool BuildLog::OpenForWriteIfNeeded() {
184 if (log_file_path_.empty()) {
187 log_file_ = fopen(log_file_path_.c_str(), "ab");
191 setvbuf(log_file_, NULL, _IOLBF, BUFSIZ);
192 SetCloseOnExec(fileno(log_file_));
194 // Opening a file in append mode doesn't set the file pointer to the file's
195 // end on Windows. Do that explicitly.
196 fseek(log_file_, 0, SEEK_END);
198 if (ftell(log_file_) == 0) {
199 if (fprintf(log_file_, kFileSignature, kCurrentVersion) < 0) {
203 log_file_path_.clear();
208 explicit LineReader(FILE* file)
209 : file_(file), buf_end_(buf_), line_start_(buf_), line_end_(NULL) {
210 memset(buf_, 0, sizeof(buf_));
213 // Reads a \n-terminated line from the file passed to the constructor.
214 // On return, *line_start points to the beginning of the next line, and
215 // *line_end points to the \n at the end of the line. If no newline is seen
216 // in a fixed buffer size, *line_end is set to NULL. Returns false on EOF.
217 bool ReadLine(char** line_start, char** line_end) {
218 if (line_start_ >= buf_end_ || !line_end_) {
219 // Buffer empty, refill.
220 size_t size_read = fread(buf_, 1, sizeof(buf_), file_);
224 buf_end_ = buf_ + size_read;
226 // Advance to next line in buffer.
227 line_start_ = line_end_ + 1;
230 line_end_ = (char*)memchr(line_start_, '\n', buf_end_ - line_start_);
232 // No newline. Move rest of data to start of buffer, fill rest.
233 size_t already_consumed = line_start_ - buf_;
234 size_t size_rest = (buf_end_ - buf_) - already_consumed;
235 memmove(buf_, line_start_, size_rest);
237 size_t read = fread(buf_ + size_rest, 1, sizeof(buf_) - size_rest, file_);
238 buf_end_ = buf_ + size_rest + read;
240 line_end_ = (char*)memchr(line_start_, '\n', buf_end_ - line_start_);
243 *line_start = line_start_;
244 *line_end = line_end_;
250 char buf_[256 << 10];
251 char* buf_end_; // Points one past the last valid byte in |buf_|.
254 // Points at the next \n in buf_ after line_start, or NULL.
258 LoadStatus BuildLog::Load(const string& path, string* err) {
259 METRIC_RECORD(".ninja_log load");
260 FILE* file = fopen(path.c_str(), "r");
263 return LOAD_NOT_FOUND;
264 *err = strerror(errno);
269 int unique_entry_count = 0;
270 int total_entry_count = 0;
272 LineReader reader(file);
273 char* line_start = 0;
275 while (reader.ReadLine(&line_start, &line_end)) {
277 sscanf(line_start, kFileSignature, &log_version);
279 if (log_version < kOldestSupportedVersion) {
280 *err = ("build log version invalid, perhaps due to being too old; "
283 unlink(path.c_str());
284 // Don't report this as a failure. An empty build log will cause
285 // us to rebuild the outputs anyway.
290 // If no newline was found in this chunk, read the next.
294 const char kFieldSeparator = '\t';
296 char* start = line_start;
297 char* end = (char*)memchr(start, kFieldSeparator, line_end - start);
302 int start_time = 0, end_time = 0;
303 TimeStamp restat_mtime = 0;
305 start_time = atoi(start);
308 end = (char*)memchr(start, kFieldSeparator, line_end - start);
312 end_time = atoi(start);
315 end = (char*)memchr(start, kFieldSeparator, line_end - start);
319 restat_mtime = strtoll(start, NULL, 10);
322 end = (char*)memchr(start, kFieldSeparator, line_end - start);
325 string output = string(start, end - start);
331 Entries::iterator i = entries_.find(output);
332 if (i != entries_.end()) {
335 entry = new LogEntry(output);
336 entries_.insert(Entries::value_type(entry->output, entry));
337 ++unique_entry_count;
341 entry->start_time = start_time;
342 entry->end_time = end_time;
343 entry->mtime = restat_mtime;
344 if (log_version >= 5) {
345 char c = *end; *end = '\0';
346 entry->command_hash = (uint64_t)strtoull(start, NULL, 16);
349 entry->command_hash = LogEntry::HashCommand(StringPiece(start,
356 return LOAD_SUCCESS; // file was empty
359 // Decide whether it's time to rebuild the log:
360 // - if we're upgrading versions
361 // - if it's getting large
362 int kMinCompactionEntryCount = 100;
363 int kCompactionRatio = 3;
364 if (log_version < kCurrentVersion) {
365 needs_recompaction_ = true;
366 } else if (total_entry_count > kMinCompactionEntryCount &&
367 total_entry_count > unique_entry_count * kCompactionRatio) {
368 needs_recompaction_ = true;
374 BuildLog::LogEntry* BuildLog::LookupByOutput(const string& path) {
375 Entries::iterator i = entries_.find(path);
376 if (i != entries_.end())
381 bool BuildLog::WriteEntry(FILE* f, const LogEntry& entry) {
382 return fprintf(f, "%d\t%d\t%" PRId64 "\t%s\t%" PRIx64 "\n",
383 entry.start_time, entry.end_time, entry.mtime,
384 entry.output.c_str(), entry.command_hash) > 0;
387 bool BuildLog::Recompact(const string& path, const BuildLogUser& user,
389 METRIC_RECORD(".ninja_log recompact");
392 string temp_path = path + ".recompact";
393 FILE* f = fopen(temp_path.c_str(), "wb");
395 *err = strerror(errno);
399 if (fprintf(f, kFileSignature, kCurrentVersion) < 0) {
400 *err = strerror(errno);
405 vector<StringPiece> dead_outputs;
406 for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
407 if (user.IsPathDead(i->first)) {
408 dead_outputs.push_back(i->first);
412 if (!WriteEntry(f, *i->second)) {
413 *err = strerror(errno);
419 for (size_t i = 0; i < dead_outputs.size(); ++i)
420 entries_.erase(dead_outputs[i]);
423 if (unlink(path.c_str()) < 0) {
424 *err = strerror(errno);
428 if (rename(temp_path.c_str(), path.c_str()) < 0) {
429 *err = strerror(errno);
436 bool BuildLog::Restat(const StringPiece path,
437 const DiskInterface& disk_interface,
438 const int output_count, char** outputs,
439 std::string* const err) {
440 METRIC_RECORD(".ninja_log restat");
443 std::string temp_path = path.AsString() + ".restat";
444 FILE* f = fopen(temp_path.c_str(), "wb");
446 *err = strerror(errno);
450 if (fprintf(f, kFileSignature, kCurrentVersion) < 0) {
451 *err = strerror(errno);
455 for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
456 bool skip = output_count > 0;
457 for (int j = 0; j < output_count; ++j) {
458 if (i->second->output == outputs[j]) {
464 const TimeStamp mtime = disk_interface.Stat(i->second->output, err);
469 i->second->mtime = mtime;
472 if (!WriteEntry(f, *i->second)) {
473 *err = strerror(errno);
480 if (unlink(path.str_) < 0) {
481 *err = strerror(errno);
485 if (rename(temp_path.c_str(), path.str_) < 0) {
486 *err = strerror(errno);