blob: 2f17f7aa451d95640c710bf184385735375d1de8 [file] [log] [blame]
Brian Silvermancb5da1f2015-12-05 22:19:58 -05001#include "aos/common/logging/implementations.h"
Brian Silvermanf665d692013-02-17 22:11:39 -08002
Brian Silvermanb0893882014-02-10 14:48:30 -08003#include <stdarg.h>
Brian4a424a22014-04-02 11:52:45 -07004#include <inttypes.h>
Austin Schuh044e18b2015-10-21 20:17:09 -07005
Brian Silvermancb5da1f2015-12-05 22:19:58 -05006#include <algorithm>
7
8#include "aos/common/die.h"
Brian Silvermanf665d692013-02-17 22:11:39 -08009#include "aos/common/once.h"
Austin Schuh044e18b2015-10-21 20:17:09 -070010#include "aos/common/time.h"
Brian Silverman669669f2014-02-14 16:32:56 -080011#include "aos/common/queue_types.h"
Brian Silvermancb5da1f2015-12-05 22:19:58 -050012#include "aos/common/logging/printf_formats.h"
13#include "aos/linux_code/ipc_lib/queue.h"
Brian Silvermanf665d692013-02-17 22:11:39 -080014
15namespace aos {
16namespace logging {
17namespace {
18
Brian Silvermanf665d692013-02-17 22:11:39 -080019// The root LogImplementation. It only logs to stderr/stdout.
20// Some of the things specified in the LogImplementation documentation doesn't
21// apply here (mostly the parts about being able to use LOG) because this is the
22// root one.
Brian Silvermancb5da1f2015-12-05 22:19:58 -050023class RootLogImplementation : public SimpleLogImplementation {
Brian Silverman2e799732014-06-05 21:50:19 -070024 public:
25 void have_other_implementation() { only_implementation_ = false; }
26
27 private:
Brian Silvermancb5da1f2015-12-05 22:19:58 -050028 void set_next(LogImplementation *) override {
Brian Silvermanf665d692013-02-17 22:11:39 -080029 LOG(FATAL, "can't have a next logger from here\n");
30 }
31
Brian Silvermanf7986142014-04-21 17:42:35 -070032 __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0)))
Brian Silvermancb5da1f2015-12-05 22:19:58 -050033 void DoLog(log_level level, const char *format, va_list ap) override {
Brian Silvermanf665d692013-02-17 22:11:39 -080034 LogMessage message;
35 internal::FillInMessage(level, format, ap, &message);
36 internal::PrintMessage(stderr, message);
Brian Silverman2e799732014-06-05 21:50:19 -070037 if (!only_implementation_) {
38 fputs("root logger got used, see stderr for message\n", stdout);
39 }
Brian Silvermanf665d692013-02-17 22:11:39 -080040 }
Brian Silverman2e799732014-06-05 21:50:19 -070041
42 bool only_implementation_ = true;
Brian Silvermanf665d692013-02-17 22:11:39 -080043};
44
Brian Silverman2e799732014-06-05 21:50:19 -070045RootLogImplementation *root_implementation = nullptr;
46
Brian Silvermanf665d692013-02-17 22:11:39 -080047void SetGlobalImplementation(LogImplementation *implementation) {
Brian Silverman5c201e22014-06-12 22:40:28 -070048 if (root_implementation == nullptr) {
49 fputs("Somebody didn't call logging::Init()!\n", stderr);
50 abort();
51 }
52
Brian Silvermancb5da1f2015-12-05 22:19:58 -050053 internal::Context *context = internal::Context::Get();
Brian Silvermanf665d692013-02-17 22:11:39 -080054
55 context->implementation = implementation;
Brian Silvermancb5da1f2015-12-05 22:19:58 -050056 internal::global_top_implementation.store(implementation);
Brian Silvermanf665d692013-02-17 22:11:39 -080057}
58
Brian Silvermane5d65692013-02-28 15:15:03 -080059void NewContext() {
Brian Silvermancb5da1f2015-12-05 22:19:58 -050060 internal::Context::Delete();
Brian Silvermane5d65692013-02-28 15:15:03 -080061}
62
Brian Silvermanf665d692013-02-17 22:11:39 -080063void *DoInit() {
Brian Silverman2e799732014-06-05 21:50:19 -070064 SetGlobalImplementation(root_implementation = new RootLogImplementation());
Brian Silvermane5d65692013-02-28 15:15:03 -080065
Brian Silvermane5d65692013-02-28 15:15:03 -080066 if (pthread_atfork(NULL /*prepare*/, NULL /*parent*/,
67 NewContext /*child*/) != 0) {
68 LOG(FATAL, "pthread_atfork(NULL, NULL, %p) failed\n",
69 NewContext);
70 }
Brian Silvermane5d65692013-02-28 15:15:03 -080071
Brian Silvermanf665d692013-02-17 22:11:39 -080072 return NULL;
73}
74
75} // namespace
76namespace internal {
Brian Silverman88471dc2014-02-15 22:35:42 -080077namespace {
Brian Silvermanf665d692013-02-17 22:11:39 -080078
Brian Silverman88471dc2014-02-15 22:35:42 -080079void FillInMessageBase(log_level level, LogMessage *message) {
Brian Silvermanf665d692013-02-17 22:11:39 -080080 Context *context = Context::Get();
81
Brian Silvermanf665d692013-02-17 22:11:39 -080082 message->level = level;
83 message->source = context->source;
Austin Schuhaebbc342015-01-25 02:25:13 -080084 memcpy(message->name, context->name, context->name_size);
85 message->name_length = context->name_size;
Brian Silvermanf665d692013-02-17 22:11:39 -080086
87 time::Time now = time::Time::Now();
88 message->seconds = now.sec();
89 message->nseconds = now.nsec();
90
91 message->sequence = context->sequence++;
92}
93
Brian Silverman88471dc2014-02-15 22:35:42 -080094} // namespace
95
96void FillInMessageStructure(log_level level,
97 const ::std::string &message_string, size_t size,
98 const MessageType *type,
99 const ::std::function<size_t(char *)> &serialize,
100 LogMessage *message) {
101 type_cache::AddShm(type->id);
102 message->structure.type_id = type->id;
103
104 FillInMessageBase(level, message);
105
106 if (message_string.size() + size > sizeof(message->structure.serialized)) {
107 LOG(FATAL, "serialized struct %s (size %zd) and message %s too big\n",
108 type->name.c_str(), size, message_string.c_str());
109 }
110 message->structure.string_length = message_string.size();
111 memcpy(message->structure.serialized, message_string.data(),
112 message->structure.string_length);
113
114 message->message_length = serialize(
115 &message->structure.serialized[message->structure.string_length]);
116 message->type = LogMessage::Type::kStruct;
117}
118
Brian Silverman664db1a2014-03-20 17:06:29 -0700119void FillInMessageMatrix(log_level level,
120 const ::std::string &message_string, uint32_t type_id,
121 int rows, int cols, const void *data,
122 LogMessage *message) {
123 CHECK(MessageType::IsPrimitive(type_id));
124 message->matrix.type = type_id;
125
126 const auto element_size = MessageType::Sizeof(type_id);
127
128 FillInMessageBase(level, message);
129
130 message->message_length = rows * cols * element_size;
131 if (message_string.size() + message->message_length >
132 sizeof(message->matrix.data)) {
133 LOG(FATAL, "%dx%d matrix of type %" PRIu32
134 " (size %u) and message %s is too big\n",
135 rows, cols, type_id, element_size, message_string.c_str());
136 }
137 message->matrix.string_length = message_string.size();
138 memcpy(message->matrix.data, message_string.data(),
139 message->matrix.string_length);
140
141 message->matrix.rows = rows;
142 message->matrix.cols = cols;
143 SerializeMatrix(type_id, &message->matrix.data[message->matrix.string_length],
144 data, rows, cols);
145 message->type = LogMessage::Type::kMatrix;
146}
147
Brian Silverman88471dc2014-02-15 22:35:42 -0800148void FillInMessage(log_level level, const char *format, va_list ap,
149 LogMessage *message) {
150 FillInMessageBase(level, message);
151
152 message->message_length =
153 ExecuteFormat(message->message, sizeof(message->message), format, ap);
154 message->type = LogMessage::Type::kString;
155}
156
Brian Silvermanf665d692013-02-17 22:11:39 -0800157void PrintMessage(FILE *output, const LogMessage &message) {
Brian Silvermana7234c62014-03-24 20:23:25 -0700158#define BASE_ARGS \
159 AOS_LOGGING_BASE_ARGS( \
160 message.name_length, message.name, static_cast<int32_t>(message.source), \
161 message.sequence, message.level, message.seconds, message.nseconds)
162 switch (message.type) {
Brian Silverman88471dc2014-02-15 22:35:42 -0800163 case LogMessage::Type::kString:
Brian Silvermana7234c62014-03-24 20:23:25 -0700164 fprintf(output, AOS_LOGGING_BASE_FORMAT "%.*s", BASE_ARGS,
Brian Silverman88471dc2014-02-15 22:35:42 -0800165 static_cast<int>(message.message_length), message.message);
166 break;
Brian Silvermanff12c9f2014-03-19 17:53:29 -0700167 case LogMessage::Type::kStruct: {
Brian Silvermanbe858a12014-04-30 17:37:28 -0700168 char buffer[2048];
Brian Silverman88471dc2014-02-15 22:35:42 -0800169 size_t output_length = sizeof(buffer);
170 size_t input_length = message.message_length;
171 if (!PrintMessage(
172 buffer, &output_length,
173 message.structure.serialized + message.structure.string_length,
174 &input_length, type_cache::Get(message.structure.type_id))) {
175 LOG(FATAL,
176 "printing message (%.*s) of type %s into %zu-byte buffer failed\n",
177 static_cast<int>(message.message_length), message.message,
178 type_cache::Get(message.structure.type_id).name.c_str(),
179 sizeof(buffer));
180 }
181 if (input_length > 0) {
182 LOG(WARNING, "%zu extra bytes on message of type %s\n", input_length,
183 type_cache::Get(message.structure.type_id).name.c_str());
184 }
Brian Silvermana7234c62014-03-24 20:23:25 -0700185 fprintf(output, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
Brian Silvermanb263d302014-02-16 00:01:43 -0800186 static_cast<int>(message.structure.string_length),
187 message.structure.serialized,
188 static_cast<int>(sizeof(buffer) - output_length), buffer);
Brian Silvermanff12c9f2014-03-19 17:53:29 -0700189 } break;
190 case LogMessage::Type::kMatrix: {
191 char buffer[1024];
192 size_t output_length = sizeof(buffer);
193 if (message.message_length !=
194 static_cast<size_t>(message.matrix.rows * message.matrix.cols *
195 MessageType::Sizeof(message.matrix.type))) {
196 LOG(FATAL, "expected %d bytes of matrix data but have %zu\n",
197 message.matrix.rows * message.matrix.cols *
198 MessageType::Sizeof(message.matrix.type),
199 message.message_length);
200 }
201 if (!PrintMatrix(buffer, &output_length,
202 message.matrix.data + message.matrix.string_length,
203 message.matrix.type, message.matrix.rows,
204 message.matrix.cols)) {
205 LOG(FATAL, "printing %dx%d matrix of type %" PRIu32 " failed\n",
206 message.matrix.rows, message.matrix.cols, message.matrix.type);
207 }
Brian Silvermana7234c62014-03-24 20:23:25 -0700208 fprintf(output, AOS_LOGGING_BASE_FORMAT "%.*s: %.*s\n", BASE_ARGS,
Brian Silvermanff12c9f2014-03-19 17:53:29 -0700209 static_cast<int>(message.matrix.string_length),
Brian Silverman664db1a2014-03-20 17:06:29 -0700210 message.matrix.data,
Brian Silvermanff12c9f2014-03-19 17:53:29 -0700211 static_cast<int>(sizeof(buffer) - output_length), buffer);
212 } break;
Brian Silverman88471dc2014-02-15 22:35:42 -0800213 }
Brian Silvermanc1a244e2014-02-20 14:12:39 -0800214#undef BASE_ARGS
Brian Silvermanf665d692013-02-17 22:11:39 -0800215}
216
217} // namespace internal
218
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500219void SimpleLogImplementation::LogStruct(
Brian Silverman669669f2014-02-14 16:32:56 -0800220 log_level level, const ::std::string &message, size_t size,
221 const MessageType *type, const ::std::function<size_t(char *)> &serialize) {
222 char serialized[1024];
223 if (size > sizeof(serialized)) {
224 LOG(FATAL, "structure of type %s too big to serialize\n",
225 type->name.c_str());
226 }
227 size_t used = serialize(serialized);
Brian Silverman2508c082014-02-17 15:45:02 -0800228 char printed[1024];
Brian Silverman669669f2014-02-14 16:32:56 -0800229 size_t printed_bytes = sizeof(printed);
230 if (!PrintMessage(printed, &printed_bytes, serialized, &used, *type)) {
231 LOG(FATAL, "PrintMessage(%p, %p(=%zd), %p, %p(=%zd), %p(name=%s)) failed\n",
232 printed, &printed_bytes, printed_bytes, serialized, &used, used, type,
233 type->name.c_str());
234 }
235 DoLogVariadic(level, "%.*s: %.*s\n", static_cast<int>(message.size()),
Brian Silverman1c7b8192014-02-16 22:37:36 -0800236 message.data(),
237 static_cast<int>(sizeof(printed) - printed_bytes), printed);
Brian Silverman669669f2014-02-14 16:32:56 -0800238}
239
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500240void SimpleLogImplementation::LogMatrix(
Brian Silvermanff12c9f2014-03-19 17:53:29 -0700241 log_level level, const ::std::string &message, uint32_t type_id,
242 int rows, int cols, const void *data) {
243 char serialized[1024];
244 if (static_cast<size_t>(rows * cols * MessageType::Sizeof(type_id)) >
245 sizeof(serialized)) {
246 LOG(FATAL, "matrix of size %u too big to serialize\n",
247 rows * cols * MessageType::Sizeof(type_id));
248 }
249 SerializeMatrix(type_id, serialized, data, rows, cols);
250 char printed[1024];
251 size_t printed_bytes = sizeof(printed);
252 if (!PrintMatrix(printed, &printed_bytes, serialized, type_id, rows, cols)) {
253 LOG(FATAL, "PrintMatrix(%p, %p(=%zd), %p, %" PRIu32 ", %d, %d) failed\n",
254 printed, &printed_bytes, printed_bytes, serialized, type_id, rows,
255 cols);
256 }
257 DoLogVariadic(level, "%.*s: %.*s\n", static_cast<int>(message.size()),
258 message.data(),
259 static_cast<int>(sizeof(printed) - printed_bytes), printed);
260}
261
Brian Silvermanbe858a12014-04-30 17:37:28 -0700262void HandleMessageLogImplementation::DoLog(log_level level, const char *format,
263 va_list ap) {
264 LogMessage message;
265 internal::FillInMessage(level, format, ap, &message);
266 HandleMessage(message);
267}
268
269void HandleMessageLogImplementation::LogStruct(
270 log_level level, const ::std::string &message_string, size_t size,
271 const MessageType *type, const ::std::function<size_t(char *)> &serialize) {
272 LogMessage message;
273 internal::FillInMessageStructure(level, message_string, size, type, serialize,
274 &message);
275 HandleMessage(message);
276}
277
278void HandleMessageLogImplementation::LogMatrix(
279 log_level level, const ::std::string &message_string, uint32_t type_id,
280 int rows, int cols, const void *data) {
281 LogMessage message;
282 internal::FillInMessageMatrix(level, message_string, type_id, rows, cols,
283 data, &message);
284 HandleMessage(message);
285}
286
Brian Silverman1e8ddfe2013-12-19 16:20:53 -0800287StreamLogImplementation::StreamLogImplementation(FILE *stream)
288 : stream_(stream) {}
289
Brian Silvermanbe858a12014-04-30 17:37:28 -0700290void StreamLogImplementation::HandleMessage(const LogMessage &message) {
Brian Silverman1e8ddfe2013-12-19 16:20:53 -0800291 internal::PrintMessage(stream_, message);
292}
293
Brian Silvermanf665d692013-02-17 22:11:39 -0800294void AddImplementation(LogImplementation *implementation) {
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500295 internal::Context *context = internal::Context::Get();
Brian Silvermanf665d692013-02-17 22:11:39 -0800296
297 if (implementation->next() != NULL) {
298 LOG(FATAL, "%p already has a next implementation, but it's not"
299 " being used yet\n", implementation);
300 }
301
302 LogImplementation *old = context->implementation;
303 if (old != NULL) {
304 implementation->set_next(old);
305 }
306 SetGlobalImplementation(implementation);
Brian Silverman2e799732014-06-05 21:50:19 -0700307 root_implementation->have_other_implementation();
Brian Silvermanf665d692013-02-17 22:11:39 -0800308}
309
310void Init() {
311 static Once<void> once(DoInit);
312 once.Get();
313}
314
315void Load() {
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500316 internal::Context::Get();
Brian Silvermanf665d692013-02-17 22:11:39 -0800317}
318
319void Cleanup() {
Brian Silvermancb5da1f2015-12-05 22:19:58 -0500320 internal::Context::Delete();
321}
322
323namespace {
324
325RawQueue *queue = NULL;
326
327int dropped_messages = 0;
328::aos::time::Time dropped_start, backoff_start;
329// Wait this long after dropping a message before even trying to write any more.
330constexpr ::aos::time::Time kDropBackoff = ::aos::time::Time::InSeconds(0.1);
331
332LogMessage *GetMessageOrDie() {
333 LogMessage *message = static_cast<LogMessage *>(queue->GetMessage());
334 if (message == NULL) {
335 LOG(FATAL, "%p->GetMessage() failed\n", queue);
336 } else {
337 return message;
338 }
339}
340
341void Write(LogMessage *msg) {
342 if (__builtin_expect(dropped_messages > 0, false)) {
343 ::aos::time::Time message_time =
344 ::aos::time::Time(msg->seconds, msg->nseconds);
345 if (message_time - backoff_start < kDropBackoff) {
346 ++dropped_messages;
347 queue->FreeMessage(msg);
348 return;
349 }
350
351 LogMessage *dropped_message = GetMessageOrDie();
352 internal::FillInMessageVarargs(
353 ERROR, dropped_message,
354 "%d logs starting at %" PRId32 ".%" PRId32 " dropped\n",
355 dropped_messages, dropped_start.sec(), dropped_start.nsec());
356 if (queue->WriteMessage(dropped_message, RawQueue::kNonBlock)) {
357 dropped_messages = 0;
358 } else {
359 // Don't even bother trying to write this message because it's not likely
360 // to work and it would be confusing to have one log in the middle of a
361 // string of failures get through.
362 ++dropped_messages;
363 backoff_start = message_time;
364 queue->FreeMessage(msg);
365 return;
366 }
367 }
368 if (!queue->WriteMessage(msg, RawQueue::kNonBlock)) {
369 if (dropped_messages == 0) {
370 dropped_start = backoff_start =
371 ::aos::time::Time(msg->seconds, msg->nseconds);
372 }
373 ++dropped_messages;
374 }
375}
376
377class LinuxQueueLogImplementation : public LogImplementation {
378 __attribute__((format(GOOD_PRINTF_FORMAT_TYPE, 3, 0)))
379 void DoLog(log_level level, const char *format, va_list ap) override {
380 LogMessage *message = GetMessageOrDie();
381 internal::FillInMessage(level, format, ap, message);
382 Write(message);
383 }
384
385 void LogStruct(log_level level, const ::std::string &message_string,
386 size_t size, const MessageType *type,
387 const ::std::function<size_t(char *)> &serialize) override {
388 LogMessage *message = GetMessageOrDie();
389 internal::FillInMessageStructure(level, message_string, size, type,
390 serialize, message);
391 Write(message);
392 }
393
394 void LogMatrix(log_level level, const ::std::string &message_string,
395 uint32_t type_id, int rows, int cols,
396 const void *data) override {
397 LogMessage *message = GetMessageOrDie();
398 internal::FillInMessageMatrix(level, message_string, type_id, rows, cols,
399 data, message);
400 Write(message);
401 }
402};
403
404} // namespace
405
406RawQueue *GetLoggingQueue() {
407 return RawQueue::Fetch("LoggingQueue", sizeof(LogMessage), 1323, 40000);
408}
409
410void RegisterQueueImplementation() {
411 Init();
412
413 queue = GetLoggingQueue();
414 if (queue == NULL) {
415 Die("logging: couldn't fetch queue\n");
416 }
417
418 AddImplementation(new LinuxQueueLogImplementation());
Brian Silvermanf665d692013-02-17 22:11:39 -0800419}
420
421} // namespace logging
422} // namespace aos