blob: 79b580d936c10f0b71388c2888f34a41f34758fa [file] [log] [blame]
John Park33858a32018-09-28 23:05:48 -07001#ifndef AOS_LOGGING_BINARY_LOG_FILE_H_
2#define AOS_LOGGING_BINARY_LOG_FILE_H_
brians343bc112013-02-10 01:53:46 +00003
brians343bc112013-02-10 01:53:46 +00004#include <sys/types.h>
Brian Silverman003ba4b2014-02-10 16:56:18 -08005#include <stddef.h>
6#include <stdint.h>
brians343bc112013-02-10 01:53:46 +00007
8#include <algorithm>
9
John Park33858a32018-09-28 23:05:48 -070010#include "aos/logging/implementations.h"
brians343bc112013-02-10 01:53:46 +000011
12namespace aos {
Brian Silvermanf665d692013-02-17 22:11:39 -080013namespace logging {
Brian Silverman003ba4b2014-02-10 16:56:18 -080014namespace linux_code {
15
16// What to align messages to. A macro because it gets used in attributes.
17// This definition gets #undefed later. Use LogFileAccessor::kAlignment instead.
18#define MESSAGE_ALIGNMENT 8
brians343bc112013-02-10 01:53:46 +000019
20// File format: {
21// LogFileMessageHeader header;
Brian Silvermanf665d692013-02-17 22:11:39 -080022// char *name; // of the process that wrote the message
Brian Silverman003ba4b2014-02-10 16:56:18 -080023// void *message;
24// } not crossing kPageSize boundaries into the file and aligned to
25// MESSAGE_ALIGNMENT.
brians343bc112013-02-10 01:53:46 +000026//
Brian Silvermanf665d692013-02-17 22:11:39 -080027// Field sizes designed to fit the various values from LogMessage even on
Brian Silverman003ba4b2014-02-10 16:56:18 -080028// other machines (hopefully) because they're baked into the files. They are
29// layed out so that all of the fields are aligned even though the whole thing
30// is packed.
Brian Silvermanf665d692013-02-17 22:11:39 -080031//
32// A lot of the fields don't have comments because they're the same as the
33// identically named fields in LogMessage.
Brian Silverman003ba4b2014-02-10 16:56:18 -080034struct __attribute__((aligned(MESSAGE_ALIGNMENT))) __attribute__((packed))
35 LogFileMessageHeader {
36 // Represents the type of an individual message.
37 enum class MessageType : uint16_t {
Brian Silverman88471dc2014-02-15 22:35:42 -080038 // char[] (no '\0' on the end).
Brian Silverman003ba4b2014-02-10 16:56:18 -080039 kString,
40 kStructType,
41 kStruct,
Brian Silverman664db1a2014-03-20 17:06:29 -070042 kMatrix,
Brian Silverman003ba4b2014-02-10 16:56:18 -080043 };
44
45 // Gets futex_set once this one has been written
46 // for readers keeping up with a live writer.
brians343bc112013-02-10 01:53:46 +000047 //
Brian Silverman003ba4b2014-02-10 16:56:18 -080048 // Gets initialized to 0 by ftruncate.
Brian Silverman65e569d2014-10-24 15:43:20 -040049 //
Brian Silverman003ba4b2014-02-10 16:56:18 -080050 // There will be something here after the last message on a "page" set to 2
51 // (by the futex_set) to indicate that the next message is on the next page.
Brian Silvermandc1eb272014-08-19 14:25:59 -040052 aos_futex marker;
brians343bc112013-02-10 01:53:46 +000053 static_assert(sizeof(marker) == 4, "mutex changed size!");
Brian Silvermandc1eb272014-08-19 14:25:59 -040054 static_assert(MESSAGE_ALIGNMENT >= alignof(aos_futex),
Brian Silverman003ba4b2014-02-10 16:56:18 -080055 "MESSAGE_ALIGNMENT is too small");
brians343bc112013-02-10 01:53:46 +000056
Brian Silvermanf665d692013-02-17 22:11:39 -080057 uint32_t time_sec;
58 static_assert(sizeof(time_sec) >= sizeof(LogMessage::seconds),
59 "tv_sec won't fit");
60 uint32_t time_nsec;
61 static_assert(sizeof(time_nsec) >= sizeof(LogMessage::nseconds),
brians343bc112013-02-10 01:53:46 +000062 "tv_nsec won't fit");
63
Brian Silvermanf665d692013-02-17 22:11:39 -080064 int32_t source;
65 static_assert(sizeof(source) >= sizeof(LogMessage::source), "PIDs won't fit");
Brian Silverman003ba4b2014-02-10 16:56:18 -080066
Brian Silverman88471dc2014-02-15 22:35:42 -080067 // Both including all of the bytes in that part of the message.
Brian Silverman003ba4b2014-02-10 16:56:18 -080068 uint32_t name_size, message_size;
69
Brian Silvermanf665d692013-02-17 22:11:39 -080070 uint16_t sequence;
71 static_assert(sizeof(sequence) == sizeof(LogMessage::sequence),
brians343bc112013-02-10 01:53:46 +000072 "something changed");
73
Brian Silverman003ba4b2014-02-10 16:56:18 -080074 MessageType type;
75
76 log_level level;
77 static_assert(sizeof(level) == 1, "log_level changed size!");
brians343bc112013-02-10 01:53:46 +000078};
79static_assert(std::is_pod<LogFileMessageHeader>::value,
80 "LogFileMessageHeader will to get dumped to a file");
Brian Silverman003ba4b2014-02-10 16:56:18 -080081static_assert(offsetof(LogFileMessageHeader, marker) == 0,
82 "marker has to be at the start so readers can find it");
brians343bc112013-02-10 01:53:46 +000083
84// Handles the mmapping and munmapping for reading and writing log files.
85class LogFileAccessor {
Brian Silverman003ba4b2014-02-10 16:56:18 -080086 public:
87 LogFileAccessor(int fd, bool writable);
Brian Silverman65e569d2014-10-24 15:43:20 -040088 ~LogFileAccessor() {
89 if (use_read_ == Maybe::kYes) {
90 delete[] current_;
91 }
92 }
Brian Silverman003ba4b2014-02-10 16:56:18 -080093
Brian Silverman003ba4b2014-02-10 16:56:18 -080094 // Asynchronously syncs all open mappings.
95 void Sync() const;
96
Brian Silvermanf5ca4d02015-03-01 16:52:24 -050097 // Returns true iff we currently have the last page in the file mapped.
98 // This is fundamentally a racy question, so the return value may not be
99 // accurate by the time this method returns.
Brian Silvermanf7780312014-02-16 17:26:15 -0800100 bool IsLastPage();
Brian Silverman003ba4b2014-02-10 16:56:18 -0800101
Brian Silvermanf5ca4d02015-03-01 16:52:24 -0500102 // Skips to the last page which is an even multiple of kSeekPages.
103 // This is fundamentally racy, so it may not actually be on the very last
104 // possible multiple of kSeekPages when it returns, but it should be close.
105 // This will never move backwards.
106 void SkipToLastSeekablePage();
107
Brian Silvermanf4452d72014-10-25 17:23:11 -0400108 size_t file_offset(const void *msg) {
109 return offset() + (static_cast<const char *>(msg) - current());
110 }
111
Brian Silvermanab5ba472014-04-18 15:26:14 -0700112 protected:
brians343bc112013-02-10 01:53:46 +0000113 // The size of the chunks that get mmaped/munmapped together. Large enough so
114 // that not too much space is wasted and it's hopefully bigger than and a
Brian Silverman003ba4b2014-02-10 16:56:18 -0800115 // multiple of the system page size but small enough so that really large
116 // chunks of memory don't have to get mapped at the same time.
117 static const size_t kPageSize = 16384;
118 // What to align messages to, copied into an actual constant.
119 static const size_t kAlignment = MESSAGE_ALIGNMENT;
120#undef MESSAGE_ALIGNMENT
Brian Silvermanf5ca4d02015-03-01 16:52:24 -0500121 // Pages which are multiples of this from the beginning of a file start with
122 // no saved state (ie struct types). This allows seeking immediately to the
123 // largest currently written interval of this number when following.
124 static const size_t kSeekPages = 256;
brians343bc112013-02-10 01:53:46 +0000125
Brian Silvermanab5ba472014-04-18 15:26:14 -0700126 char *current() const { return current_; }
127 size_t position() const { return position_; }
128 off_t offset() const { return offset_; }
129
130 void IncrementPosition(size_t size) {
131 position_ += size;
132 AlignPosition();
133 }
134
135 void MapNextPage();
136 void Unmap(void *location);
137
138 // Advances position to the next (aligned) location.
139 void AlignPosition() {
140 position_ += kAlignment - (position_ % kAlignment);
141 }
142
Brian Silverman65e569d2014-10-24 15:43:20 -0400143 protected:
144 bool definitely_use_read() const { return use_read_ == Maybe::kYes; }
145 bool definitely_use_mmap() const { return use_read_ == Maybe::kNo; }
146
Brian Silvermanab5ba472014-04-18 15:26:14 -0700147 private:
Brian Silverman65e569d2014-10-24 15:43:20 -0400148 // Used for representing things that we might know to be true/false or we
149 // might not know (yet).
150 enum class Maybe { kUnknown, kYes, kNo };
151
brians343bc112013-02-10 01:53:46 +0000152 const int fd_;
153 const bool writable_;
154
Brian Silverman003ba4b2014-02-10 16:56:18 -0800155 // Into the file. Always a multiple of kPageSize.
156 off_t offset_;
brians343bc112013-02-10 01:53:46 +0000157 char *current_;
158 size_t position_;
159
Brian Silverman65e569d2014-10-24 15:43:20 -0400160 Maybe is_last_page_ = Maybe::kUnknown;
161
162 // Use read instead of mmap (necessary for fds that don't support mmap).
163 Maybe use_read_ = Maybe::kUnknown;
Brian Silvermanab5ba472014-04-18 15:26:14 -0700164};
Brian Silvermanf7780312014-02-16 17:26:15 -0800165
Brian Silvermanab5ba472014-04-18 15:26:14 -0700166class LogFileReader : public LogFileAccessor {
167 public:
168 LogFileReader(int fd) : LogFileAccessor(fd, false) {}
169
170 // May return NULL iff wait is false.
171 const LogFileMessageHeader *ReadNextMessage(bool wait);
172
173 private:
Brian Silverman8a2e7142014-03-12 22:17:34 -0700174 // Tries reading from the current page to see if it fails because the file
175 // isn't big enough.
176 void CheckCurrentPageReadable();
Brian Silvermanab5ba472014-04-18 15:26:14 -0700177};
brians343bc112013-02-10 01:53:46 +0000178
Brian Silvermanab5ba472014-04-18 15:26:14 -0700179class LogFileWriter : public LogFileAccessor {
180 public:
181 LogFileWriter(int fd) : LogFileAccessor(fd, true) {}
182
183 // message_size should be the total number of bytes needed for the message.
184 LogFileMessageHeader *GetWritePosition(size_t message_size);
Brian Silvermanf5ca4d02015-03-01 16:52:24 -0500185
186 // Returns true exactly once for each unique cookie on each page where cached
187 // data should be cleared.
188 // Call with a non-zero next_message_size to determine if cached data should
189 // be forgotten before writing a next_message_size-sized message.
190 // cookie should be initialized to 0.
191 bool ShouldClearSeekableData(off_t *cookie, size_t next_message_size) const;
192
193 // Forces a move to a new page for the next message.
194 // This is important when there is cacheable data that needs to be re-written
195 // before a message which will spill over onto the next page but the cacheable
196 // message being refreshed is smaller and won't get to a new page by itself.
197 void ForceNewPage();
198
199 private:
200 bool NeedNewPageFor(size_t bytes) const;
brians343bc112013-02-10 01:53:46 +0000201};
202
Brian Silverman003ba4b2014-02-10 16:56:18 -0800203} // namespace linux_code
Brian Silvermanf665d692013-02-17 22:11:39 -0800204} // namespace logging
205} // namespace aos
brians343bc112013-02-10 01:53:46 +0000206
John Park33858a32018-09-28 23:05:48 -0700207#endif // AOS_LOGGING_BINARY_LOG_FILE_H_