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
51namespace {
52
53const char kFileSignature[] = "# ninja log v%d\n";
54const int kOldestSupportedVersion = 7;
55const int kCurrentVersion = 7;
56
57} // namespace
58
59// static
61 return rapidhash(command.str_, command.len_);
62}
63
65
70
71BuildLog::BuildLog() = default;
72
76
77bool BuildLog::OpenForWrite(const std::string& path, const BuildLogUser& user,
78 std::string* err) {
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
90bool 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
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;
174 buf_end_ = buf_ + size_read;
175 } else {
176 // Advance to next line in buffer.
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;
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
204 // Points at the next \n in buf_ after line_start, or NULL.
206};
207
208LoadStatus 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
328 Entries::iterator i = entries_.find(path);
329 if (i != entries_.end())
330 return i->second.get();
331 return NULL;
332}
333
334bool 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
340bool 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
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 buf_[256<< 10]
Definition build_log.cc:200
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.
const char * str_
std::string AsString() const
Convert the slice into a full-fledged std::string, copying the data into a new string.
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