blob: 9ea5c820f18b01429e9ca6cc15e3d9ec964987c4 [file] [log] [blame]
John Park33858a32018-09-28 23:05:48 -07001#include "aos/logging/binary_log_file.h"
Brian Silverman003ba4b2014-02-10 16:56:18 -08002
3#include <stdio.h>
Brian Silverman003ba4b2014-02-10 16:56:18 -08004#include <string.h>
5#include <sys/mman.h>
6#include <sys/stat.h>
7#include <unistd.h>
Brian Silverman8a2e7142014-03-12 22:17:34 -07008#include <signal.h>
9#include <setjmp.h>
Brian Silverman003ba4b2014-02-10 16:56:18 -080010
11namespace aos {
12namespace logging {
13namespace linux_code {
14namespace {
15
16unsigned long SystemPageSize() {
17 static unsigned long r = sysconf(_SC_PAGESIZE);
18 return r;
19}
20
21} // namespace
22
23LogFileAccessor::LogFileAccessor(int fd, bool writable)
24 : fd_(fd), writable_(writable), offset_(0), current_(0), position_(0) {
25 // Check to make sure that mmap will allow mmaping in chunks of kPageSize.
26 if (SystemPageSize() > kPageSize || (kPageSize % SystemPageSize()) != 0) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070027 AOS_LOG(FATAL, "system page size (%lu) not factor of kPageSize (%zd).\n",
28 SystemPageSize(), kPageSize);
Brian Silverman003ba4b2014-02-10 16:56:18 -080029 }
30
31 MapNextPage();
32}
33
Brian Silverman003ba4b2014-02-10 16:56:18 -080034void LogFileAccessor::Sync() const {
35 msync(current_, kPageSize, MS_ASYNC | MS_INVALIDATE);
36}
37
Brian Silvermanf5ca4d02015-03-01 16:52:24 -050038void LogFileAccessor::SkipToLastSeekablePage() {
Austin Schuhf257f3c2019-10-27 21:00:43 -070039 AOS_CHECK(definitely_use_mmap());
Brian Silvermanf5ca4d02015-03-01 16:52:24 -050040
41 struct stat info;
42 if (fstat(fd_, &info) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070043 AOS_PLOG(FATAL, "fstat(%d, %p) failed", fd_, &info);
Brian Silvermanf5ca4d02015-03-01 16:52:24 -050044 }
45
Austin Schuhf257f3c2019-10-27 21:00:43 -070046 AOS_CHECK((info.st_size % kPageSize) == 0);
Brian Silvermanf5ca4d02015-03-01 16:52:24 -050047 const auto last_readable_page_number = (info.st_size / kPageSize) - 1;
48 const auto last_seekable_page_number =
49 last_readable_page_number / kSeekPages * kSeekPages;
50 const off_t new_offset = last_seekable_page_number * kPageSize;
51 // We don't want to go backwards...
52 if (new_offset > offset_) {
53 Unmap(current_);
54 offset_ = new_offset;
55 MapNextPage();
56 }
57}
58
59// The only way to tell is using fstat, but we don't really want to be making a
60// syscall every single time somebody wants to know the answer, so it gets
61// cached in is_last_page_.
Brian Silvermanf7780312014-02-16 17:26:15 -080062bool LogFileAccessor::IsLastPage() {
Brian Silverman65e569d2014-10-24 15:43:20 -040063 if (is_last_page_ != Maybe::kUnknown) {
64 return is_last_page_ == Maybe::kYes;
Brian Silvermanf7780312014-02-16 17:26:15 -080065 }
66
Brian Silverman003ba4b2014-02-10 16:56:18 -080067 struct stat info;
68 if (fstat(fd_, &info) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070069 AOS_PLOG(FATAL, "fstat(%d, %p) failed", fd_, &info);
Brian Silverman003ba4b2014-02-10 16:56:18 -080070 }
Brian Silverman97478342014-02-16 20:26:31 -080071 bool r = offset_ == static_cast<off_t>(info.st_size - kPageSize);
Brian Silverman65e569d2014-10-24 15:43:20 -040072 is_last_page_ = r ? Maybe::kYes : Maybe::kNo;
Brian Silvermanf7780312014-02-16 17:26:15 -080073 return r;
Brian Silverman003ba4b2014-02-10 16:56:18 -080074}
75
76void LogFileAccessor::MapNextPage() {
77 if (writable_) {
78 if (ftruncate(fd_, offset_ + kPageSize) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070079 AOS_PLOG(FATAL, "ftruncate(%d, %zd) failed", fd_, kPageSize);
Brian Silverman003ba4b2014-02-10 16:56:18 -080080 }
81 }
Brian Silverman65e569d2014-10-24 15:43:20 -040082
83 if (use_read_ == Maybe::kYes) {
84 ssize_t todo = kPageSize;
85 while (todo > 0) {
86 ssize_t result = read(fd_, current_ + (kPageSize - todo), todo);
87 if (result == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070088 AOS_PLOG(FATAL, "read(%d, %p, %zu) failed", fd_,
89 current_ + (kPageSize - todo), todo);
Brian Silverman5c222b62014-12-20 16:42:54 -080090 } else if (result == 0) {
91 memset(current_, 0, todo);
92 result = todo;
Brian Silverman65e569d2014-10-24 15:43:20 -040093 }
94 todo -= result;
95 }
Austin Schuhf257f3c2019-10-27 21:00:43 -070096 AOS_CHECK_EQ(0, todo);
Brian Silverman65e569d2014-10-24 15:43:20 -040097 } else {
98 current_ = static_cast<char *>(
99 mmap(NULL, kPageSize, PROT_READ | (writable_ ? PROT_WRITE : 0),
100 MAP_SHARED, fd_, offset_));
101 if (current_ == MAP_FAILED) {
102 if (!writable_ && use_read_ == Maybe::kUnknown && errno == ENODEV) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700103 AOS_LOG(INFO, "Falling back to reading the file using read(2).\n");
Brian Silverman65e569d2014-10-24 15:43:20 -0400104 use_read_ = Maybe::kYes;
105 current_ = new char[kPageSize];
106 MapNextPage();
107 return;
108 } else {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700109 AOS_PLOG(
110 FATAL,
111 "mmap(NULL, %zd, PROT_READ [ | PROT_WRITE], MAP_SHARED, %d, %jd)"
112 " failed",
113 kPageSize, fd_, static_cast<intmax_t>(offset_));
Brian Silverman65e569d2014-10-24 15:43:20 -0400114 }
115 } else {
116 use_read_ = Maybe::kNo;
117 }
118 if (madvise(current_, kPageSize, MADV_SEQUENTIAL | MADV_WILLNEED) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700119 AOS_PLOG(WARNING,
120 "madvise(%p, %zd, MADV_SEQUENTIAL | MADV_WILLNEED) failed",
121 current_, kPageSize);
Brian Silverman65e569d2014-10-24 15:43:20 -0400122 }
Brian Silverman8a2e7142014-03-12 22:17:34 -0700123 }
Brian Silverman003ba4b2014-02-10 16:56:18 -0800124 offset_ += kPageSize;
125}
126
127void LogFileAccessor::Unmap(void *location) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700128 AOS_CHECK_NE(Maybe::kUnknown, use_read_);
Brian Silverman65e569d2014-10-24 15:43:20 -0400129
130 if (use_read_ == Maybe::kNo) {
131 if (munmap(location, kPageSize) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700132 AOS_PLOG(FATAL, "munmap(%p, %zd) failed", location, kPageSize);
Brian Silverman65e569d2014-10-24 15:43:20 -0400133 }
Brian Silverman003ba4b2014-02-10 16:56:18 -0800134 }
Brian Silverman65e569d2014-10-24 15:43:20 -0400135 is_last_page_ = Maybe::kUnknown;
Brian Silvermanab5ba472014-04-18 15:26:14 -0700136 position_ = 0;
137}
138
139const LogFileMessageHeader *LogFileReader::ReadNextMessage(bool wait) {
140 LogFileMessageHeader *r;
141 do {
142 r = static_cast<LogFileMessageHeader *>(
143 static_cast<void *>(&current()[position()]));
144 if (wait) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700145 AOS_CHECK(definitely_use_mmap());
Brian Silvermanab5ba472014-04-18 15:26:14 -0700146 if (futex_wait(&r->marker) != 0) continue;
147 }
148 if (r->marker == 2) {
149 Unmap(current());
150 MapNextPage();
151 CheckCurrentPageReadable();
152 r = static_cast<LogFileMessageHeader *>(static_cast<void *>(current()));
153 }
154 } while (wait && r->marker == 0);
155 if (r->marker == 0) {
156 return NULL;
157 }
158 IncrementPosition(sizeof(LogFileMessageHeader) + r->name_size +
159 r->message_size);
160 if (position() >= kPageSize) {
161 // It's a lot better to blow up here rather than getting SIGBUS errors the
162 // next time we try to read...
Austin Schuhf257f3c2019-10-27 21:00:43 -0700163 AOS_LOG(FATAL, "corrupt log file running over page size\n");
Brian Silvermanab5ba472014-04-18 15:26:14 -0700164 }
165 return r;
Brian Silverman003ba4b2014-02-10 16:56:18 -0800166}
167
Brian Silverman8a2e7142014-03-12 22:17:34 -0700168// This mess is because the only not completely hackish way to do this is to set
169// up a handler for SIGBUS/SIGSEGV that siglongjmps out to avoid either the
170// instruction being repeated infinitely (and more signals being delivered) or
171// (with SA_RESETHAND) the signal killing the process.
172namespace {
173
174void *volatile fault_address;
175sigjmp_buf jump_context;
176
177void CheckCurrentPageReadableHandler(int /*signal*/, siginfo_t *info, void *) {
178 fault_address = info->si_addr;
179
180 siglongjmp(jump_context, 1);
181}
182
183} // namespace
Brian Silvermanab5ba472014-04-18 15:26:14 -0700184void LogFileReader::CheckCurrentPageReadable() {
Brian Silverman65e569d2014-10-24 15:43:20 -0400185 if (definitely_use_read()) return;
186
Brian Silverman8a2e7142014-03-12 22:17:34 -0700187 if (sigsetjmp(jump_context, 1) == 0) {
188 struct sigaction action;
189 action.sa_sigaction = CheckCurrentPageReadableHandler;
190 sigemptyset(&action.sa_mask);
191 action.sa_flags = SA_RESETHAND | SA_SIGINFO;
192 struct sigaction previous_bus, previous_segv;
193 if (sigaction(SIGBUS, &action, &previous_bus) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700194 AOS_PLOG(FATAL, "sigaction(SIGBUS(=%d), %p, %p) failed", SIGBUS, &action,
195 &previous_bus);
Brian Silverman8a2e7142014-03-12 22:17:34 -0700196 }
197 if (sigaction(SIGSEGV, &action, &previous_segv) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700198 AOS_PLOG(FATAL, "sigaction(SIGSEGV(=%d), %p, %p) failed", SIGSEGV,
199 &action, &previous_segv);
Brian Silverman8a2e7142014-03-12 22:17:34 -0700200 }
201
Brian Silvermanab5ba472014-04-18 15:26:14 -0700202 char __attribute__((unused)) c = current()[0];
Brian Silverman8a2e7142014-03-12 22:17:34 -0700203
204 if (sigaction(SIGBUS, &previous_bus, NULL) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700205 AOS_PLOG(FATAL, "sigaction(SIGBUS(=%d), %p, NULL) failed", SIGBUS,
206 &previous_bus);
Brian Silverman8a2e7142014-03-12 22:17:34 -0700207 }
208 if (sigaction(SIGSEGV, &previous_segv, NULL) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700209 AOS_PLOG(FATAL, "sigaction(SIGSEGV(=%d), %p, NULL) failed", SIGSEGV,
210 &previous_segv);
Brian Silverman8a2e7142014-03-12 22:17:34 -0700211 }
212 } else {
Brian Silvermanab5ba472014-04-18 15:26:14 -0700213 if (fault_address == current()) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700214 AOS_LOG(FATAL, "could not read 1 byte at offset 0x%jx into log file\n",
215 static_cast<uintmax_t>(offset()));
Brian Silverman8a2e7142014-03-12 22:17:34 -0700216 } else {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700217 AOS_LOG(FATAL, "faulted at %p, not %p like we were (maybe) supposed to\n",
218 fault_address, current());
Brian Silverman8a2e7142014-03-12 22:17:34 -0700219 }
220 }
221}
222
Brian Silvermanab5ba472014-04-18 15:26:14 -0700223LogFileMessageHeader *LogFileWriter::GetWritePosition(size_t message_size) {
Brian Silvermanf5ca4d02015-03-01 16:52:24 -0500224 if (NeedNewPageFor(message_size)) ForceNewPage();
Brian Silvermanab5ba472014-04-18 15:26:14 -0700225 LogFileMessageHeader *const r = static_cast<LogFileMessageHeader *>(
226 static_cast<void *>(&current()[position()]));
227 IncrementPosition(message_size);
228 return r;
229}
230
Brian Silvermanf5ca4d02015-03-01 16:52:24 -0500231// A number of seekable pages, not the actual file offset, is stored in *cookie.
232bool LogFileWriter::ShouldClearSeekableData(off_t *cookie,
233 size_t next_message_size) const {
234 off_t next_message_page = (offset() / kPageSize) - 1;
235 if (NeedNewPageFor(next_message_size)) {
236 ++next_message_page;
237 }
238 const off_t current_seekable_page = next_message_page / kSeekPages;
Austin Schuhf257f3c2019-10-27 21:00:43 -0700239 AOS_CHECK_LE(*cookie, current_seekable_page);
Brian Silvermanf5ca4d02015-03-01 16:52:24 -0500240 const bool r = *cookie != current_seekable_page;
241 *cookie = current_seekable_page;
242 return r;
243}
244
245bool LogFileWriter::NeedNewPageFor(size_t bytes) const {
246 return position() + bytes + (kAlignment - (bytes % kAlignment)) +
247 sizeof(aos_futex) >
248 kPageSize;
249}
250
251void LogFileWriter::ForceNewPage() {
252 char *const temp = current();
253 MapNextPage();
254 if (futex_set_value(
255 static_cast<aos_futex *>(static_cast<void *>(&temp[position()])),
256 2) == -1) {
Austin Schuhf257f3c2019-10-27 21:00:43 -0700257 AOS_PLOG(WARNING, "readers will hang because futex_set_value(%p, 2) failed",
258 &temp[position()]);
Brian Silvermanf5ca4d02015-03-01 16:52:24 -0500259 }
260 Unmap(temp);
261}
262
Brian Silverman003ba4b2014-02-10 16:56:18 -0800263} // namespace linux_code
264} // namespace logging
265} // namespace aos