• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = 6;
57 const int kCurrentVersion = 6;
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 mtime)118 BuildLog::LogEntry::LogEntry(const string& output, uint64_t command_hash,
119     int start_time, int end_time, TimeStamp mtime)
120     : output(output), command_hash(command_hash),
121         start_time(start_time), end_time(end_time), mtime(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             bool invalid_log_version = false;
283             if (log_version < kOldestSupportedVersion) {
284                 invalid_log_version = true;
285                 *err = "build log version is too old; starting over";
286 
287             } else if (log_version > kCurrentVersion) {
288                 invalid_log_version = true;
289                 *err = "build log version is too new; starting over";
290             }
291             if (invalid_log_version) {
292                 fclose(file);
293                 unlink(path.c_str());
294                 // Don't report this as a failure. A missing build log will cause
295                 // us to rebuild the outputs anyway.
296                 return LOAD_NOT_FOUND;
297             }
298         }
299 
300         // If no newline was found in this chunk, read the next.
301         if (!line_end)
302             continue;
303 
304         const char kFieldSeparator = '\t';
305 
306         char* start = line_start;
307         char* end = (char*)memchr(start, kFieldSeparator, line_end - start);
308         if (!end)
309             continue;
310         *end = 0;
311 
312         int start_time = 0, end_time = 0;
313         TimeStamp mtime = 0;
314 
315         start_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         end_time = atoi(start);
323         start = end + 1;
324 
325         end = (char*)memchr(start, kFieldSeparator, line_end - start);
326         if (!end)
327             continue;
328         *end = 0;
329         mtime = strtoll(start, NULL, 10);
330         start = end + 1;
331 
332         end = (char*)memchr(start, kFieldSeparator, line_end - start);
333         if (!end)
334             continue;
335         string output = string(start, end - start);
336 
337         start = end + 1;
338         end = line_end;
339 
340         LogEntry* entry;
341         Entries::iterator i = entries_.find(output);
342         if (i != entries_.end()) {
343             entry = i->second;
344         } else {
345             entry = new LogEntry(output);
346             entries_.insert(Entries::value_type(entry->output, entry));
347             ++unique_entry_count;
348         }
349         ++total_entry_count;
350 
351         entry->start_time = start_time;
352         entry->end_time = end_time;
353         entry->mtime = mtime;
354         char c = *end; *end = '\0';
355         entry->command_hash = (uint64_t)strtoull(start, NULL, 16);
356         *end = c;
357     }
358     fclose(file);
359 
360     if (!line_start) {
361         return LOAD_SUCCESS; // file was empty
362     }
363 
364     // Decide whether it's time to rebuild the log:
365     // - if we're upgrading versions
366     // - if it's getting large
367     int kMinCompactionEntryCount = 100;
368     int kCompactionRatio = 3;
369     if (log_version < kCurrentVersion) {
370         needs_recompaction_ = true;
371     } else if (total_entry_count > kMinCompactionEntryCount &&
372                           total_entry_count > unique_entry_count * kCompactionRatio) {
373         needs_recompaction_ = true;
374     }
375 
376     return LOAD_SUCCESS;
377 }
378 
LookupByOutput(const string & path)379 BuildLog::LogEntry* BuildLog::LookupByOutput(const string& path) {
380     Entries::iterator i = entries_.find(path);
381     if (i != entries_.end())
382         return i->second;
383     return NULL;
384 }
385 
WriteEntry(FILE * f,const LogEntry & entry)386 bool BuildLog::WriteEntry(FILE* f, const LogEntry& entry) {
387     return fprintf(f, "%d\t%d\t%" PRId64 "\t%s\t%" PRIx64 "\n",
388                     entry.start_time, entry.end_time, entry.mtime,
389                     entry.output.c_str(), entry.command_hash) > 0;
390 }
391 
Recompact(const string & path,const BuildLogUser & user,string * err)392 bool BuildLog::Recompact(const string& path, const BuildLogUser& user,
393                                                   string* err) {
394     METRIC_RECORD(".ninja_log recompact");
395 
396     Close();
397     string temp_path = path + ".recompact";
398     FILE* f = fopen(temp_path.c_str(), "wb");
399     if (!f) {
400         *err = strerror(errno);
401         return false;
402     }
403 
404     if (fprintf(f, kFileSignature, kCurrentVersion) < 0) {
405         *err = strerror(errno);
406         fclose(f);
407         return false;
408     }
409 
410     vector<StringPiece> dead_outputs;
411     for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
412         if (user.IsPathDead(i->first)) {
413             dead_outputs.push_back(i->first);
414             continue;
415         }
416 
417         if (!WriteEntry(f, *i->second)) {
418             *err = strerror(errno);
419             fclose(f);
420             return false;
421         }
422     }
423 
424     for (size_t i = 0; i < dead_outputs.size(); ++i)
425         entries_.erase(dead_outputs[i]);
426 
427     fclose(f);
428     if (unlink(path.c_str()) < 0) {
429         *err = strerror(errno);
430         return false;
431     }
432 
433     if (rename(temp_path.c_str(), path.c_str()) < 0) {
434         *err = strerror(errno);
435         return false;
436     }
437 
438     return true;
439 }
440 
Restat(const StringPiece path,const DiskInterface & disk_interface,const int output_count,char ** outputs,std::string * const err)441 bool BuildLog::Restat(const StringPiece path,
442                                             const DiskInterface& disk_interface,
443                                             const int output_count, char** outputs,
444                                             std::string* const err) {
445     METRIC_RECORD(".ninja_log restat");
446 
447     Close();
448     std::string temp_path = path.AsString() + ".restat";
449     FILE* f = fopen(temp_path.c_str(), "wb");
450     if (!f) {
451         *err = strerror(errno);
452         return false;
453     }
454 
455     if (fprintf(f, kFileSignature, kCurrentVersion) < 0) {
456         *err = strerror(errno);
457         fclose(f);
458         return false;
459     }
460     for (Entries::iterator i = entries_.begin(); i != entries_.end(); ++i) {
461         bool skip = output_count > 0;
462         for (int j = 0; j < output_count; ++j) {
463             if (i->second->output == outputs[j]) {
464                 skip = false;
465                 break;
466             }
467         }
468         if (!skip) {
469             const TimeStamp mtime = disk_interface.Stat(i->second->output, err);
470             if (mtime == -1) {
471                 fclose(f);
472                 return false;
473             }
474             i->second->mtime = mtime;
475         }
476 
477         if (!WriteEntry(f, *i->second)) {
478             *err = strerror(errno);
479             fclose(f);
480             return false;
481         }
482     }
483 
484     fclose(f);
485     if (unlink(path.str_) < 0) {
486         *err = strerror(errno);
487         return false;
488     }
489 
490     if (rename(temp_path.c_str(), path.str_) < 0) {
491         *err = strerror(errno);
492         return false;
493     }
494 
495     return true;
496 }
497