1 /**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
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
16 #ifndef LIBPANDABASE_UTILS_LOGGER_H
17 #define LIBPANDABASE_UTILS_LOGGER_H
18
19 #include "macros.h"
20 #include "os/error.h"
21 #include "os/mutex.h"
22 #include "os/thread.h"
23
24 #include <array>
25 #include <cstdint>
26
27 #include <bitset>
28 #include <fstream>
29 #include <map>
30 #include <string>
31 #include <sstream>
32
33 #include <atomic>
34
35 #ifdef ENABLE_HILOG
36 #if defined(__clang__)
37 #pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
38 #elif defined(__GNUC__)
39 #pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
40 #endif
41
42 #include <hilog/log.h>
43
44 #undef LOG_DOMAIN
45 #define LOG_DOMAIN 0xD003F00
46
47 #undef LOG_TAG
48 #define LOG_TAG "ArkCompiler"
49 #endif
50
51 namespace panda {
52
53 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
54 #define LOG_COMPONENT_ELEM(D, NAME, STR) D(NAME, NAME, STR)
55
56 using FUNC_MOBILE_LOG_PRINT = int (*)(int, int, const char *, const char *, const char *);
57 constexpr int LOG_ID_MAIN = 0;
58 extern FUNC_MOBILE_LOG_PRINT mlog_buf_print;
59
60 namespace base_options {
61 class Options;
62 } // namespace base_options
63
64 class Logger {
65 public:
66 #include <logger_enum_gen.h>
67
68 using ComponentMask = std::bitset<Component::LAST>;
69
70 enum PandaLog2MobileLog : int {
71 UNKNOWN = 0,
72 DEFAULT,
73 VERBOSE,
74 DEBUG,
75 INFO,
76 WARN,
77 ERROR,
78 FATAL,
79 SILENT,
80 };
81
82 class Buffer {
83 public:
84 constexpr static size_t BUFFER_SIZE = 4096;
85
86 public:
Buffer()87 Buffer() : buffer {} {}
88
89 public:
90 const char *data() const noexcept
91 {
92 return buffer.data();
93 }
94 char *data() noexcept
95 {
96 return buffer.data();
97 }
98
99 public:
100 constexpr size_t size() const noexcept
101 {
102 return BUFFER_SIZE;
103 }
104
105 public:
106 // always overwrites buffer data
107 Buffer &printf(const char *format, ...);
108
109 public:
operator <<(std::ostream &os, const Buffer &b)110 friend std::ostream &operator<<(std::ostream &os, const Buffer &b)
111 {
112 return os << b.data();
113 }
114
115 private:
116 std::array<char, BUFFER_SIZE> buffer;
117 };
118
119 class Message {
120 public:
Message(Level level, Component component, bool print_system_error)121 Message(Level level, Component component, bool print_system_error)
122 : level_(level), component_(component), print_system_error_(print_system_error)
123 {
124 #ifndef NDEBUG
125 Logger::LogNestingInc();
126 #endif
127 }
128
129 ~Message();
130
GetStream()131 std::ostream &GetStream()
132 {
133 return stream_;
134 }
135
136 private:
137 Level level_;
138 Component component_;
139 bool print_system_error_;
140 std::ostringstream stream_;
141
142 NO_COPY_SEMANTIC(Message);
143 NO_MOVE_SEMANTIC(Message);
144 };
145
146 static void Initialize(const base_options::Options &options);
147
148 static void InitializeFileLogging(const std::string &log_file, Level level, const ComponentMask &component_mask,
149 bool is_fast_logging = false);
150 #ifdef ENABLE_HILOG
151 static void InitializeHiLogging(Level level, const ComponentMask &component_mask);
152 #endif
153
154 static void InitializeStdLogging(Level level, const ComponentMask &component_mask);
155
156 static void InitializeDummyLogging(Level level = Level::DEBUG, const ComponentMask &component_mask = 0);
157
158 static void Destroy();
159
SetMobileLogPrintEntryPointByPtr(void *mlog_buf_print_ptr)160 static void SetMobileLogPrintEntryPointByPtr(void *mlog_buf_print_ptr)
161 {
162 mlog_buf_print = reinterpret_cast<FUNC_MOBILE_LOG_PRINT>(mlog_buf_print_ptr);
163 }
164
165 static uint32_t GetLevelNumber(Logger::Level level);
166
WriteMobileLog(Level level, const char *component, const char *message)167 void WriteMobileLog(Level level, const char *component, const char *message)
168 {
169 if (mlog_buf_print == nullptr || !is_mlog_opened_) {
170 return;
171 }
172 PandaLog2MobileLog mlog_level = PandaLog2MobileLog::UNKNOWN;
173 switch (level) {
174 case Level::DEBUG:
175 mlog_level = PandaLog2MobileLog::DEBUG;
176 break;
177 case Level::INFO:
178 mlog_level = PandaLog2MobileLog::INFO;
179 break;
180 case Level::ERROR:
181 mlog_level = PandaLog2MobileLog::ERROR;
182 break;
183 case Level::FATAL:
184 mlog_level = PandaLog2MobileLog::FATAL;
185 break;
186 case Level::WARNING:
187 mlog_level = PandaLog2MobileLog::WARN;
188 break;
189 default:
190 UNREACHABLE();
191 }
192 std::string panda_component = "Ark " + std::string(component);
193 mlog_buf_print(LOG_ID_MAIN, mlog_level, panda_component.c_str(), "%s", message);
194 }
195
IsLoggingOn(Level level, Component component)196 static bool IsLoggingOn(Level level, Component component)
197 {
198 return IsInitialized() && level <= logger->level_ &&
199 (logger->component_mask_.test(component) || level == Level::FATAL);
200 }
201
IsLoggingOnOrAbort(Level level, Component component)202 static bool IsLoggingOnOrAbort(Level level, Component component)
203 {
204 if (IsLoggingOn(level, component)) {
205 return true;
206 }
207
208 if (level == Level::FATAL) {
209 std::abort();
210 }
211
212 return false;
213 }
214
215 #ifndef NDEBUG
216 static void LogNestingInc();
217 static void LogNestingDec();
218 static bool IsMessageSuppressed([[maybe_unused]] Level level, [[maybe_unused]] Component component);
219 #endif
220
221 static void Log(Level level, Component component, const std::string &str);
222
Sync()223 static void Sync()
224 {
225 if (IsInitialized()) {
226 logger->SyncOutputResource();
227 }
228 }
229
230 static Level LevelFromString(std::string_view s);
231
232 static ComponentMask ComponentMaskFromString(std::string_view s);
233
234 static std::string StringfromDfxComponent(LogDfxComponent dfx_component);
235
SetLevel(Level level)236 static void SetLevel(Level level)
237 {
238 ASSERT(IsInitialized());
239 logger->level_ = level;
240 }
241
GetLevel()242 static Level GetLevel()
243 {
244 ASSERT(IsInitialized());
245 return logger->level_;
246 }
247
EnableComponent(Component component)248 static void EnableComponent(Component component)
249 {
250 ASSERT(IsInitialized());
251 logger->component_mask_.set(component);
252 }
253
EnableComponent(const ComponentMask &component)254 static void EnableComponent(const ComponentMask &component)
255 {
256 ASSERT(IsInitialized());
257 logger->component_mask_ |= component;
258 }
259
DisableComponent(Component component)260 static void DisableComponent(Component component)
261 {
262 ASSERT(IsInitialized());
263 logger->component_mask_.reset(component);
264 }
265
ResetComponentMask()266 static void ResetComponentMask()
267 {
268 ASSERT(IsInitialized());
269 logger->component_mask_.reset();
270 }
271
SetMobileLogOpenFlag(bool is_mlog_opened)272 static void SetMobileLogOpenFlag(bool is_mlog_opened)
273 {
274 ASSERT(IsInitialized());
275 logger->is_mlog_opened_ = is_mlog_opened;
276 }
277
278 static bool IsInLevelList(std::string_view s);
279
280 static bool IsInComponentList(std::string_view s);
281
282 static void ProcessLogLevelFromString(std::string_view s);
283
284 static void ProcessLogComponentsFromString(std::string_view s);
285
IsInitialized()286 static bool IsInitialized()
287 {
288 return logger != nullptr;
289 }
290
291 protected:
Logger(Level level, const ComponentMask &component_mask)292 Logger(Level level, const ComponentMask &component_mask)
293 : level_(level),
294 component_mask_(component_mask)
295 #ifndef NDEBUG
296 ,
297 // Means all the LOGs are allowed just as usual
298 nested_allowed_level_(Level::LAST)
299 #endif
300 {
301 }
302
Logger(Level level, const ComponentMask &component_mask, [[maybe_unused]] Level nested_allowed_level)303 Logger(Level level, const ComponentMask &component_mask, [[maybe_unused]] Level nested_allowed_level)
304 : level_(level),
305 component_mask_(component_mask)
306 #ifndef NDEBUG
307 ,
308 nested_allowed_level_(nested_allowed_level)
309 #endif
310 {
311 }
312
313 virtual void LogLineInternal(Level level, Component component, const std::string &str) = 0;
314
315 /**
316 * Flushes all the output buffers of LogLineInternal to the output resources
317 * Sometimes nothinig shall be done, if LogLineInternal flushes everything by itself statelessl
318 */
319 virtual void SyncOutputResource() = 0;
320
321 virtual ~Logger() = default;
322
323 static Logger *logger;
324
325 static os::memory::Mutex mutex;
326
327 static thread_local int nesting;
328
329 private:
330 Level level_;
331 ComponentMask component_mask_;
332 #ifndef NDEBUG
333 // These are utilized by Fast* logger types.
334 // For every thread, we trace events of staring shifting to a log (<<) and finishing doing it,
335 // incrementing a log invocation depth variable bound to a thread, or decrementing it correspondingly.
336 // Such variables we're doing as thread-local.
337 // All the LOGs with levels < nested_allowed_level_ are only allowed to have depth of log == 1
338 Level nested_allowed_level_; // Log level to suppress LOG triggering within << to another LOG
339 #endif
340 bool is_mlog_opened_ {true};
341
342 NO_COPY_SEMANTIC(Logger);
343 NO_MOVE_SEMANTIC(Logger);
344 };
345
346 static Logger::ComponentMask LoggerComponentMaskAll = ~Logger::ComponentMask();
347
348 class FileLogger : public Logger {
349 protected:
FileLogger(std::ofstream &&stream, Level level, const ComponentMask &component_mask)350 FileLogger(std::ofstream &&stream, Level level, const ComponentMask &component_mask)
351 : Logger(level, component_mask), stream_(std::forward<std::ofstream>(stream))
352 {
353 }
354
355 void LogLineInternal(Level level, Component component, const std::string &str) override;
356 void SyncOutputResource() override {}
357
358 ~FileLogger() override = default;
359
360 NO_COPY_SEMANTIC(FileLogger);
361 NO_MOVE_SEMANTIC(FileLogger);
362
363 private:
364 std::ofstream stream_;
365
366 friend Logger;
367 };
368
369 class FastFileLogger : public Logger {
370 protected:
371 // Uses advanced Logger constructor, so we tell to suppress all nested messages below WARNING severity
FastFileLogger(std::ofstream &&stream, Level level, const ComponentMask &component_mask)372 FastFileLogger(std::ofstream &&stream, Level level, const ComponentMask &component_mask)
373 : Logger(level, component_mask, Logger::Level::WARNING), stream_(std::forward<std::ofstream>(stream))
374 {
375 }
376
377 void LogLineInternal(Level level, Component component, const std::string &str) override;
378 void SyncOutputResource() override;
379
380 ~FastFileLogger() override = default;
381
382 NO_COPY_SEMANTIC(FastFileLogger);
383 NO_MOVE_SEMANTIC(FastFileLogger);
384
385 private:
386 std::ofstream stream_;
387
388 friend Logger;
389 };
390
391 #ifdef ENABLE_HILOG
392 class HiLogger : public Logger {
393 protected:
HiLogger(Level level, const ComponentMask &component_mask)394 HiLogger(Level level, const ComponentMask &component_mask) : Logger(level, component_mask) {}
395
396 void LogLineInternal(Level level, Component component, const std::string &str) override;
397 void SyncOutputResource() override {}
398
399 ~HiLogger() override = default;
400
401 NO_COPY_SEMANTIC(HiLogger);
402 NO_MOVE_SEMANTIC(HiLogger);
403
404 private:
405 std::ostringstream stream_;
406
407 friend Logger;
408 };
409 #endif
410
411 class StderrLogger : public Logger {
412 private:
StderrLogger(Level level, const ComponentMask &component_mask)413 StderrLogger(Level level, const ComponentMask &component_mask) : Logger(level, component_mask) {}
414
415 void LogLineInternal(Level level, Component component, const std::string &str) override;
416 void SyncOutputResource() override {}
417
418 friend Logger;
419
420 ~StderrLogger() override = default;
421
422 NO_COPY_SEMANTIC(StderrLogger);
423 NO_MOVE_SEMANTIC(StderrLogger);
424 };
425
426 class DummyLogger : public Logger {
427 private:
DummyLogger(Level level, const ComponentMask &component_mask)428 DummyLogger(Level level, const ComponentMask &component_mask) : Logger(level, component_mask) {}
429
430 void LogLineInternal([[maybe_unused]] Level level, [[maybe_unused]] Component component,
431 [[maybe_unused]] const std::string &str) override
432 {
433 }
434
435 void SyncOutputResource() override {}
436
437 friend Logger;
438
439 ~DummyLogger() override = default;
440
441 NO_COPY_SEMANTIC(DummyLogger);
442 NO_MOVE_SEMANTIC(DummyLogger);
443 };
444
445 class DummyStream {
446 public:
operator bool() const447 explicit operator bool() const
448 {
449 return true;
450 }
451 };
452
453 template <class T>
operator <<(DummyStream s, [[maybe_unused]] const T &v)454 DummyStream operator<<(DummyStream s, [[maybe_unused]] const T &v)
455 {
456 return s;
457 }
458
459 class LogOnceHelper {
460 public:
IsFirstCall()461 bool IsFirstCall()
462 {
463 flag_ >>= 1U;
464 return flag_ != 0;
465 }
466
467 private:
468 uint8_t flag_ = 0x03;
469 };
470
471 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
472 #define LOG_ONCE_HELPER() static LogOnceHelper MERGE_WORDS(log_once_helper, __LINE__);
473
474 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
475 #define LOG_ONCE(level, component) \
476 LOG_ONCE_HELPER() \
477 MERGE_WORDS(log_once_helper, __LINE__).IsFirstCall() && LOG(level, component)
478
479 #ifndef NDEBUG
480 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
481 #define _LOG_SUPPRESSION_CHECK(level, component) \
482 !panda::Logger::IsMessageSuppressed(panda::Logger::Level::level, panda::Logger::Component::component)
483 #else
484 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
485 #define _LOG_SUPPRESSION_CHECK(level, component) true
486 #endif
487
488 // Explicit namespace is specified to allow using the logger out of panda namespace.
489 // For example, in the main function.
490 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
491 #define _LOG(level, component, p) \
492 panda::Logger::IsLoggingOnOrAbort(panda::Logger::Level::level, panda::Logger::Component::component) && \
493 _LOG_SUPPRESSION_CHECK(level, component) && \
494 panda::Logger::Message(panda::Logger::Level::level, panda::Logger::Component::component, p).GetStream()
495
496 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
497 #define LOG_TRANSFORM(level, component) _LOG_##level(component, false)
498 #define LOG(level, component) LOG_TRANSFORM(level, component)
499
500 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
501 #define GET_LOG_STREAM(level, component) \
502 panda::Logger::Message(panda::Logger::Level::level, panda::Logger::Component::component, false).GetStream()
503
504 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
505 #define PLOG(level, component) _LOG_##level(component, true)
506
507 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
508 #define LOG_IF(cond, level, component) (cond) && LOG(level, component)
509
510 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
511 #define PLOG_IF(cond, level, component) (cond) && PLOG(level, component)
512
513 #ifndef NDEBUG
514
515 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
516 #define _LOG_DEBUG(component, p) _LOG(DEBUG, component, p)
517
518 #else
519
520 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
521 #define _LOG_DEBUG(component, p) false && panda::DummyStream()
522
523 #endif
524
525 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
526 #define _LOG_INFO(component, p) _LOG(INFO, component, p)
527
528 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
529 #define _LOG_WARNING(component, p) _LOG(WARNING, component, p)
530
531 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
532 #define _LOG_ERROR(component, p) _LOG(ERROR, component, p)
533
534 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
535 #define _LOG_FATAL(component, p) _LOG(FATAL, component, p)
536
537 } // namespace panda
538
539 #endif // LIBPANDABASE_UTILS_LOGGER_H
540