xref: /third_party/ninja/src/build_log.cc (revision 695b41ee)
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
44using 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
53namespace {
54
55const char kFileSignature[] = "# ninja log v%d\n";
56const int kOldestSupportedVersion = 6;
57const 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)
65inline
66uint64_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
111uint64_t BuildLog::LogEntry::HashCommand(StringPiece command) {
112    return MurmurHash64A(command.str_, command.len_);
113}
114
115BuildLog::LogEntry::LogEntry(const string& output)
116    : output(output) {}
117
118BuildLog::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
124BuildLog::BuildLog()
125    : log_file_(NULL), needs_recompaction_(false) {}
126
127BuildLog::~BuildLog() {
128    Close();
129}
130
131bool 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
144bool 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
178void 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
185bool 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
210struct LineReader {
211    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.
220    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
261LoadStatus 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
379BuildLog::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
386bool 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
392bool 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
441bool 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