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 using namespace std;
45
46 // Implementation details:
47 // Each run's log appends to the log file.
48 // To load, we run through all log entries in series, throwing away
49 // older runs.
50 // Once the number of redundant entries exceeds a threshold, we write
51 // out a new file and replace the existing one with it.
52
53 namespace {
54
55 const char kFileSignature[] = "# ninja log v%d\n";
56 const int kOldestSupportedVersion = 4;
57 const int kCurrentVersion = 5;
58
59 // 64bit MurmurHash2, by Austin Appleby
60 #if defined(_MSC_VER)
61 #define BIG_CONSTANT(x) (x)
62 #else // defined(_MSC_VER)
63 #define BIG_CONSTANT(x) (x##LLU)
64 #endif // !defined(_MSC_VER)
65 inline
MurmurHash64A(const void * key,size_t len)66 uint64_t MurmurHash64A(const void* key, size_t len) {
67 static const uint64_t seed = 0xDECAFBADDECAFBADull;
68 const uint64_t m = BIG_CONSTANT(0xc6a4a7935bd1e995);
69 const int r = 47;
70 uint64_t h = seed ^ (len * m);
71 const unsigned char* data = (const unsigned char*)key;
72 while (len >= 8) {
73 uint64_t k;
74 memcpy(&k, data, sizeof k);
75 k *= m;
76 k ^= k >> r;
77 k *= m;
78 h ^= k;
79 h *= m;
80 data += 8;
81 len -= 8;
82 }
83 switch (len & 7)
84 {
85 case 7: h ^= uint64_t(data[6]) << 48;
86 NINJA_FALLTHROUGH;
87 case 6: h ^= uint64_t(data[5]) << 40;
88 NINJA_FALLTHROUGH;
89 case 5: h ^= uint64_t(data[4]) << 32;
90 NINJA_FALLTHROUGH;
91 case 4: h ^= uint64_t(data[3]) << 24;
92 NINJA_FALLTHROUGH;
93 case 3: h ^= uint64_t(data[2]) << 16;
94 NINJA_FALLTHROUGH;
95 case 2: h ^= uint64_t(data[1]) << 8;
96 NINJA_FALLTHROUGH;
97 case 1: h ^= uint64_t(data[0]);
98 h *= m;
99 };
100 h ^= h >> r;
101 h *= m;
102 h ^= h >> r;
103 return h;
104 }
105 #undef BIG_CONSTANT
106
107
108 } // namespace
109
110 // static
HashCommand(StringPiece command)111 uint64_t BuildLog::LogEntry::HashCommand(StringPiece command) {
112 return MurmurHash64A(command.str_, command.len_);
113 }
114
LogEntry(const string & output)115 BuildLog::LogEntry::LogEntry(const string& output)
116 : output(output) {}
117
LogEntry(const string & output,uint64_t command_hash,int start_time,int end_time,TimeStamp restat_mtime)118 BuildLog::LogEntry::LogEntry(const string& output, uint64_t command_hash,
119 int start_time, int end_time, TimeStamp restat_mtime)
120 : output(output), command_hash(command_hash),
121 start_time(start_time), end_time(end_time), mtime(restat_mtime)
122 {}
123
BuildLog()124 BuildLog::BuildLog()
125 : log_file_(NULL), needs_recompaction_(false) {}
126
~BuildLog()127 BuildLog::~BuildLog() {
128 Close();
129 }
130
OpenForWrite(const string & path,const BuildLogUser & user,string * err)131 bool BuildLog::OpenForWrite(const string& path, const BuildLogUser& user,
132 string* err) {
133 if (needs_recompaction_) {
134 if (!Recompact(path, user, err))
135 return false;
136 }
137
138 assert(!log_file_);
139 log_file_path_ = path; // we don't actually open the file right now, but will
140 // do so on the first write attempt
141 return true;
142 }
143
RecordCommand(Edge * edge,int start_time,int end_time,TimeStamp mtime)144 bool BuildLog::RecordCommand(Edge* edge, int start_time, int end_time,
145 TimeStamp mtime) {
146 string command = edge->EvaluateCommand(true);
147 uint64_t command_hash = LogEntry::HashCommand(command);
148 for (vector<Node*>::iterator out = edge->outputs_.begin();
149 out != edge->outputs_.end(); ++out) {
150 const string& path = (*out)->path();
151 Entries::iterator i = entries_.find(path);
152 LogEntry* log_entry;
153 if (i != entries_.end()) {
154 log_entry = i->second;
155 } else {
156 log_entry = new LogEntry(path);
157 entries_.insert(Entries::value_type(log_entry->output, log_entry));
158 }
159 log_entry->command_hash = command_hash;
160 log_entry->start_time = start_time;
161 log_entry->end_time = end_time;
162 log_entry->mtime = mtime;
163
164 if (!OpenForWriteIfNeeded()) {
165 return false;
166 }
167 if (log_file_) {
168 if (!WriteEntry(log_file_, *log_entry))
169 return false;
170 if (fflush(log_file_) != 0) {
171 return false;
172 }
173 }
174 }
175 return true;
176 }
177
Close()178 void BuildLog::Close() {
179 OpenForWriteIfNeeded(); // create the file even if nothing has been recorded
180 if (log_file_)
181 fclose(log_file_);
182 log_file_ = NULL;
183 }
184
OpenForWriteIfNeeded()185 bool BuildLog::OpenForWriteIfNeeded() {
186 if (log_file_ || log_file_path_.empty()) {
187 return true;
188 }
189 log_file_ = fopen(log_file_path_.c_str(), "ab");
190 if (!log_file_) {
191 return false;
192 }
193 if (setvbuf(log_file_, NULL, _IOLBF, BUFSIZ) != 0) {
194 return false;
195 }
196 SetCloseOnExec(fileno(log_file_));
197
198 // Opening a file in append mode doesn't set the file pointer to the file's
199 // end on Windows. Do that explicitly.
200 fseek(log_file_, 0, SEEK_END);
201
202 if (ftell(log_file_) == 0) {
203 if (fprintf(log_file_, kFileSignature, kCurrentVersion) < 0) {
204 return false;
205 }
206 }
207 return true;
208 }
209
210 struct LineReader {
LineReaderLineReader211 explicit LineReader(FILE* file)
212 : file_(file), buf_end_(buf_), line_start_(buf_), line_end_(NULL) {
213 memset(buf_, 0, sizeof(buf_));
214 }
215
216 // Reads a \n-terminated line from the file passed to the constructor.
217 // On return, *line_start points to the beginning of the next line, and
218 // *line_end points to the \n at the end of the line. If no newline is seen
219 // in a fixed buffer size, *line_end is set to NULL. Returns false on EOF.
ReadLineLineReader220 bool ReadLine(char** line_start, char** line_end) {
221 if (line_start_ >= buf_end_ || !line_end_) {
222 // Buffer empty, refill.
223 size_t size_read = fread(buf_, 1, sizeof(buf_), file_);
224 if (!size_read)
225 return false;
226 line_start_ = buf_;
227 buf_end_ = buf_ + size_read;
228 } else {
229 // Advance to next line in buffer.
230 line_start_ = line_end_ + 1;
231 }
232
233 line_end_ = (char*)memchr(line_start_, '\n', buf_end_ - line_start_);
234 if (!line_end_) {
235 // No newline. Move rest of data to start of buffer, fill rest.
236 size_t already_consumed = line_start_ - buf_;
237 size_t size_rest = (buf_end_ - buf_) - already_consumed;
238 memmove(buf_, line_start_, size_rest);
239
240 size_t read = fread(buf_ + size_rest, 1, sizeof(buf_) - size_rest, file_);
241 buf_end_ = buf_ + size_rest + read;
242 line_start_ = buf_;
243 line_end_ = (char*)memchr(line_start_, '\n', buf_end_ - line_start_);
244 }
245
246 *line_start = line_start_;
247 *line_end = line_end_;
248 return true;
249 }
250
251 private:
252 FILE* file_;
253 char buf_[256 << 10];
254 char* buf_end_; // Points one past the last valid byte in |buf_|.
255
256 char* line_start_;
257 // Points at the next \n in buf_ after line_start, or NULL.
258 char* line_end_;
259 };
260
Load(const string & path,string * err)261 LoadStatus BuildLog::Load(const string& path, string* err) {
262 METRIC_RECORD(".ninja_log load");
263 FILE* file = fopen(path.c_str(), "r");
264 if (!file) {
265 if (errno == ENOENT)
266 return LOAD_NOT_FOUND;
267 *err = strerror(errno);
268 return LOAD_ERROR;
269 }
270
271 int log_version = 0;
272 int unique_entry_count = 0;
273 int total_entry_count = 0;
274
275 LineReader reader(file);
276 char* line_start = 0;
277 char* line_end = 0;
278 while (reader.ReadLine(&line_start, &line_end)) {
279 if (!log_version) {
280 sscanf(line_start, kFileSignature, &log_version);
281
282 if (log_version < kOldestSupportedVersion) {
283 *err = ("build log version invalid, perhaps due to being too old; "
284 "starting over");
285 fclose(file);
286 unlink(path.c_str());
287 // Don't report this as a failure. An empty build log will cause
288 // us to rebuild the outputs anyway.
289 return LOAD_SUCCESS;
290 }
291 }
292
293 // If no newline was found in this chunk, read the next.
294 if (!line_end)
295 continue;
296
297 const char kFieldSeparator = '\t';
298
299 char* start = line_start;
300 char* end = (char*)memchr(start, kFieldSeparator, line_end - start);
301 if (!end)
302 continue;
303 *end = 0;
304
305 int start_time = 0, end_time = 0;
306 TimeStamp restat_mtime = 0;
307
308 start_time = atoi(start);
309 start = end + 1;
310
311 end = (char*)memchr(start, kFieldSeparator, line_end - start);
312 if (!end)
313 continue;
314 *end = 0;
315 end_time = atoi(start);
316 start = end + 1;
317
318 end = (char*)memchr(start, kFieldSeparator, line_end - start);
319 if (!end)
320 continue;
321 *end = 0;
322 restat_mtime = strtoll(start, NULL, 10);
323 start = end + 1;
324
325 end = (char*)memchr(start, kFieldSeparator, line_end - start);
326 if (!end)
327 continue;
328 string output = string(start, end - start);
329
330 start = end + 1;
331 end = line_end;
332
333 LogEntry* entry;
334 Entries::iterator i = entries_.find(output);
335 if (i != entries_.end()) {
336 entry = i->second;
337 } else {
338 entry = new LogEntry(output);
339 entries_.insert(Entries::value_type(entry->output, entry));
340 ++unique_entry_count;
341 }
342 ++total_entry_count;
343
344 entry->start_time = start_time;
345 entry->end_time = end_time;
346 entry->mtime = restat_mtime;
347 if (log_version >= 5) {
348 char c = *end; *end = '\0';
349 entry->command_hash = (uint64_t)strtoull(start, NULL, 16);
350 *end = c;
351 } else {
352 entry->command_hash = LogEntry::HashCommand(StringPiece(start,
353 end - start));
354 }
355 }
356 fclose(file);
357
358 if (!line_start) {
359 return LOAD_SUCCESS; // file was empty
360 }
361
362 // Decide whether it's time to rebuild the log:
363 // - if we're upgrading versions
364 // - if it's getting large
365 int kMinCompactionEntryCount = 100;
366 int kCompactionRatio = 3;
367 if (log_version < kCurrentVersion) {
368 needs_recompaction_ = true;
369 } else if (total_entry_count > kMinCompactionEntryCount &&
370 total_entry_count > unique_entry_count * kCompactionRatio) {
371 needs_recompaction_ = true;
372 }
373
374 return LOAD_SUCCESS;
375 }
376
LookupByOutput(const string & path)377 BuildLog::LogEntry* BuildLog::LookupByOutput(const string& path) {
378 Entries::iterator i = entries_.find(path);
379 if (i != entries_.end())
380 return i->second;
381 return NULL;
382 }
383
WriteEntry(FILE * f,const LogEntry & entry)384 bool BuildLog::WriteEntry(FILE* f, const LogEntry& entry) {
385 return fprintf(f, "%d\t%d\t%" PRId64 "\t%s\t%" PRIx64 "\n",
386 entry.start_time, entry.end_time, entry.mtime,
387 entry.output.c_str(), entry.command_hash) > 0;
388 }
389
Recompact(const string & path,const BuildLogUser & user,string * err)390 bool BuildLog::Recompact(const string& path, const BuildLogUser& user,
391 string* err) {
392 METRIC_RECORD(".ninja_log recompact");
393
394 Close();
395 string temp_path = path + ".recompact";
396 FILE* f = fopen(temp_path.c_str(), "wb");
397 if (!f) {
398 *err = strerror(errno);
399 return false;
400 }
401
402 if (fprintf(f, kFileSignature, kCurrentVersion) < 0) {
403 *err = strerror(errno);
404 fclose(f);
405 return false;
406 }
407
408 vector<StringPiece> dead_outputs;
409 for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
410 if (user.IsPathDead(i->first)) {
411 dead_outputs.push_back(i->first);
412 continue;
413 }
414
415 if (!WriteEntry(f, *i->second)) {
416 *err = strerror(errno);
417 fclose(f);
418 return false;
419 }
420 }
421
422 for (size_t i = 0; i < dead_outputs.size(); ++i)
423 entries_.erase(dead_outputs[i]);
424
425 fclose(f);
426 if (unlink(path.c_str()) < 0) {
427 *err = strerror(errno);
428 return false;
429 }
430
431 if (rename(temp_path.c_str(), path.c_str()) < 0) {
432 *err = strerror(errno);
433 return false;
434 }
435
436 return true;
437 }
438
Restat(const StringPiece path,const DiskInterface & disk_interface,const int output_count,char ** outputs,std::string * const err)439 bool BuildLog::Restat(const StringPiece path,
440 const DiskInterface& disk_interface,
441 const int output_count, char** outputs,
442 std::string* const err) {
443 METRIC_RECORD(".ninja_log restat");
444
445 Close();
446 std::string temp_path = path.AsString() + ".restat";
447 FILE* f = fopen(temp_path.c_str(), "wb");
448 if (!f) {
449 *err = strerror(errno);
450 return false;
451 }
452
453 if (fprintf(f, kFileSignature, kCurrentVersion) < 0) {
454 *err = strerror(errno);
455 fclose(f);
456 return false;
457 }
458 for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
459 bool skip = output_count > 0;
460 for (int j = 0; j < output_count; ++j) {
461 if (i->second->output == outputs[j]) {
462 skip = false;
463 break;
464 }
465 }
466 if (!skip) {
467 const TimeStamp mtime = disk_interface.Stat(i->second->output, err);
468 if (mtime == -1) {
469 fclose(f);
470 return false;
471 }
472 i->second->mtime = mtime;
473 }
474
475 if (!WriteEntry(f, *i->second)) {
476 *err = strerror(errno);
477 fclose(f);
478 return false;
479 }
480 }
481
482 fclose(f);
483 if (unlink(path.str_) < 0) {
484 *err = strerror(errno);
485 return false;
486 }
487
488 if (rename(temp_path.c_str(), path.str_) < 0) {
489 *err = strerror(errno);
490 return false;
491 }
492
493 return true;
494 }
495