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