1 /*
2  * Copyright (c) 2023 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 MAPLE_UTIL_INCLUDE_MPL_LOGGING_H
17 #define MAPLE_UTIL_INCLUDE_MPL_LOGGING_H
18 
19 #include <string>
20 #include <cstdio>
21 #include <stdarg.h>
22 #include <sstream>
23 #include <iostream>
24 
25 // This file defines the APIs that govern all messaging-styled output from
26 // a running program under MAPLE, which can be a compiler or a runtime component.
27 //
28 // There are two overall classes of messages:
29 //
30 // (1) Those of interest only to developers, and thus should be suppressed in the
31 // production release version of the software.  The message content will include
32 // the source file and line number of the trigger point of the message.
33 //
34 // (2) Those intended to be visible to users of the software in general,  in
35 // addition to the developers.
36 //
37 // Messages are divided into 6 types, or levels, from 0 to 5.  Conceptually,
38 // the lower the level, the higher the frequency of occurrences, the larger the
39 // output volume, the smaller the audience of interest and the more often they need
40 // to be filtered out. In addition, the higher the level, the greater the severity.
41 //
42 // Level 0 (DBG) - These are messages for debugging purposes, used by the
43 // developer during development to debug his code.
44 //
45 // Level 1 (LOG) - These are log messages, also for helping the developer in
46 // debugging, but at a higher level of algorithmic operation.
47 //
48 // Level 2 (INFO) - These provide information that are of general usefulness in
49 // the normal operation of the SW.
50 //
51 // Level 3 (WARN) - These provide warning messages.
52 //
53 // Level 4 (ERR) - These provide error messages.
54 //
55 // Level 5 (FATAL) - These indicate errors of such severity that the program r
56 // execution cannot continue.
57 //
58 // DBG and LOG are only for developers' use.  INFO, WARN, ERR and FATAL are
59 // intended for general visibility.  There is an additional type of ERR that
60 // designate developer errors that arise out of checking code inserted by the
61 // developers, which has the following 4 usage patterns:
62 //
63 // CHECK - If the specified program condition is not satisfied, output the error
64 // message. The program will continue execution.
65 //
66 // DCHECK - Same as CHECK, but the check is suppressed in the release version of
67 // the SW.
68 //
69 // CHECK_FATAL - If the specified program condition is not satisfied, output the error
70 // message. The program will stop execution at that point.
71 //
72 // DEBUG_ASSERT - Same as CHECK_FATAL, but the check is suppressed in the release version of
73 // the SW.
74 //
75 // The macro setting DEBUG=1 picks developer and DEBUG=0 picks release builds.
76 //
77 // the macro PRINT_LEVEL_DEV is a filter for DBG messages in developer builds.
78 // When PRINT_LEVEL_DEV is set to kLlLog, DBG messages are not printed.
79 //
80 // Instantiated object c_logInfo, of class LogInfo, provides finer control of
81 // the logging behavior during execution.  Use emitLogDevice() to change the
82 // message destination.  Use setLogMode() to change the verbosity of the messages.
83 //
84 // In the case of DBG and LOG, the message needs to print the name of the SW
85 // component as the volume of messages can be huge.  Use enum LOG_TAGS to define
86 // the component ID and its name string.
87 //
88 // Since INFO, WARN, ERR and FATAL are for general consumption, each message
89 // should provide an number code under enum LogNumberCode.
90 //
91 // Following are usage of logging actions supported:
92 //
93 // GDB,LOG,INFO,WARN,ERR,FATAL can be invoked as method.
94 //   parameters:
95 //     TAG
96 //     formatted string
97 //     variadic list
98 //
99 // CHECK,DCHECK,CHECK_FATAL,DEBUG_ASSERT also can be invoked as method.
100 //   parameters:
101 //     condition
102 //     formatted string
103 //     variadic list
104 //
105 // Each of the above are mapped to one of the following 3 methods in class LogInfo:
106 //
107 // EmitLogForDev() - for DBG and LOG
108 //
109 // EmitLogForUser() - for INFO, WARN, ERR and FATAL
110 //
111 // EmitErrorMessage() - for CHECK, DCHECK, CHECK_FATAL and DEBUG_ASSERT
112 //
113 // DBG and LOG send their messages to stdout, and provide additional date and time
114 // information.  For the rest, the messages are sent to stderr.
115 //
116 // In debugging the program, the developer can set breakpoint in one of the above
117 // 3 methods depending on the type of message. For DEBUG_ASSERT, abort() is called
118 // instead of exit(1) so that the program will not completely exit, to allow the
119 // developer to print stack trace and peruse the program environment at the point
120 // of the assertion.
121 namespace maple {
122 extern class LogInfo logInfo;
123 extern class LogInfo &log;
124 
125 enum LogLevel { kLlDbg, kLlLog, kLlInfo, kLlWarn, kLlErr, kLlFatal, kLlMax };
126 
127 enum LogTags { kLtThread, kLtLooper, kLtAll };
128 
129 enum LogMode { kLmSimple, kLmComplex, kLmMax };
130 
131 enum LogNumberCode { kLncInfo = 0, kLncWarn = 20, kLncErr = 40, kLncFatal = 60, kLncMax = 99 };
132 
133 class LogInfo {
134 public:
LogInfo()135     LogInfo() : outStream(stdout), outMode(kLmComplex) {}
136     LogInfo(const LogInfo &p) = delete;
137     LogInfo &operator=(const LogInfo &p) = delete;
138 
~LogInfo()139     ~LogInfo()
140     {
141         fclose(outStream);
142     }
143 
144     static std::ostream &MapleLogger(LogLevel level = kLlLog);
145     void EmitLogForUser(enum LogNumberCode num, enum LogLevel ll, const char *fmt, ...) const;
146     void EmitLogForUser(enum LogNumberCode num, enum LogLevel ll, const std::string &message) const;
147     void EmitErrorMessage(const std::string &cond, const std::string &file, unsigned int line, const char *fmt,
148                           ...) const;
149 
150 private:
SetLogDevice(FILE &stream)151     void SetLogDevice(FILE &stream)
152     {
153         outStream = &stream;
154     }
SetLogMode(LogMode lm)155     void SetLogMode(LogMode lm)
156     {
157         outMode = lm;
158     }
159     FILE *outStream;
160     LogMode outMode;
161 };
162 
163 #ifdef DEBUG // no debug in default
164 #define DEBUG_STMT(x) x
165 #define DEBUG_TEST 1
166 #define ENABLE_ASSERT 1
167 #else
168 #define DEBUG_STMT(x)
169 #define DEBUG_TEST 0
170 #define ENABLE_ASSERT 0
171 #endif  // DEBUG
172 
173 // for developer
174 #define PRINT_LEVEL_DEV kLlLog
175 
176 #ifndef IS_RELEASE_VERSION
177 #define DBG(tag, fmt, ...)                                                                                 \
178     do {                                                                                                   \
179         if (PRINT_LEVEL_DEV <= kLlLog) {                                                                   \
180             logInfo.EmitLogForDev(tag, kLlLog, __FILE_NAME__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__); \
181         }                                                                                                  \
182     } while (0)
183 #else
184 #define DBG(tag, fmt, ...)
185 #endif // IS_RELEASE_VERSION
186 
187 // #ifdef LOG
188 // #undef LOG
189 // #endif
190 // #define LOG(tag, fmt, ...)                                                                      \
191 //   do {                                                                                          \
192 //     if (PRINT_LEVEL_DEV <= kLlLog) {                                                            \
193 //       logInfo.EmitLogForDev(tag, kLlLog, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__); \
194 //     }                                                                                                \
195 //   } while (0)
196 
197 #ifdef CHECK
198 #undef CHECK
199 #endif
200 
201 #ifndef IS_RELEASE_VERSION
202 #define CHECK(cond, fmt, ...)                                                                  \
203     do {                                                                                       \
204         if (!(cond)) {                                                                         \
205             logInfo.EmitErrorMessage(#cond, __FILE_NAME__, __LINE__, fmt "\n", ##__VA_ARGS__); \
206         }                                                                                      \
207     } while (0)
208 #else
209 #define CHECK(cond, fmt, ...)                                                                  \
210     do {                                                                                       \
211         if (!(cond)) {}                                                                        \
212     } while (0)
213 #endif // IS_RELEASE_VERSION
214 
215 #ifdef DCHECK
216 #undef DCHECK
217 #endif
218 #define DCHECK(cond, fmt, ...)                       \
219     do {                                             \
220         DEBUG_STMT(CHECK(cond, fmt, ##__VA_ARGS__)); \
221     } while (0)
222 
223 // To shut down the codecheck warning: boolean condition for 'if' always evaluates to 'true'
224 #ifndef IS_RELEASE_VERSION
225 #define CHECK_FATAL_FALSE(fmt, ...)                                                                 \
226     do {                                                                                            \
227         maple::logInfo.EmitErrorMessage("false", __FILE_NAME__, __LINE__, fmt "\n", ##__VA_ARGS__); \
228         exit(1);                                                                                    \
229     } while (0)
230 
231 #define CHECK_FATAL(cond, fmt, ...)                                                                   \
232     do {                                                                                              \
233         if (!(cond)) {                                                                                \
234             maple::logInfo.EmitErrorMessage(#cond, __FILE_NAME__, __LINE__, fmt "\n", ##__VA_ARGS__); \
235             if (DEBUG_TEST != 0) {                                                                    \
236                 abort();                                                                              \
237             } else {                                                                                  \
238                 exit(1);                                                                              \
239             }                                                                                         \
240         }                                                                                             \
241     } while (0)
242 #else
243 #define CHECK_FATAL_FALSE(fmt, ...)                                                            \
244     do {                                                                                       \
245         exit(1);                                                                               \
246     } while (0)
247 
248 #define CHECK_FATAL(cond, fmt, ...)                                                              \
249     do {                                                                                         \
250         if (!(cond)) {                                                                           \
251             if (DEBUG_TEST != 0) {                                                               \
252                 abort();                                                                         \
253             } else {                                                                             \
254                 exit(1);                                                                         \
255             }                                                                                    \
256         }                                                                                        \
257     } while (0)
258 #endif // IS_RELEASE_VERSION
259 
260 #define CHECK_NULL_FATAL(ptr) CHECK_FATAL((ptr) != nullptr, "Failed with nullptr.")
261 
262 #if ENABLE_ASSERT // assert not enabled in default
263 #define DEBUG_ASSERT(cond, fmt, ...)                                                                  \
264     do {                                                                                              \
265         if (!(cond)) {                                                                                \
266             maple::logInfo.EmitErrorMessage(#cond, __FILE_NAME__, __LINE__, fmt "\n", ##__VA_ARGS__); \
267             abort();                                                                                  \
268         }                                                                                             \
269     } while (0)
270 
271 #define ASSERT_NOT_NULL(ptr) DEBUG_ASSERT((ptr) != nullptr, "Failed with nullptr.")
272 #else
273 #define DEBUG_ASSERT(cond, fmt, ...)
274 #define ASSERT_NOT_NULL(ptr)
275 #endif  // ENABLE_ASSERT
276 
277 // for user
278 #define PRINT_LEVEL_USER kLlInfo
279 
280 #define INFO(num, fmt, ...)                                           \
281     do {                                                              \
282         if (PRINT_LEVEL_USER <= kLlInfo) {                            \
283             logInfo.EmitLogForUser(num, kLlInfo, fmt, ##__VA_ARGS__); \
284         }                                                             \
285     } while (0)
286 
287 #define INFO_V(verbose, num, fmt, ...)                                \
288     if (verbose) {                                                    \
289         if (PRINT_LEVEL_USER <= kLlInfo) {                            \
290             logInfo.EmitLogForUser(num, kLlInfo, fmt, ##__VA_ARGS__); \
291         }                                                             \
292     }
293 
294 #define WARN(num, fmt, ...)                                           \
295     do {                                                              \
296         if (PRINT_LEVEL_USER <= kLlWarn) {                            \
297             logInfo.EmitLogForUser(num, kLlWarn, fmt, ##__VA_ARGS__); \
298         }                                                             \
299     } while (0)
300 
301 #define ERR(num, fmt, ...)                                           \
302     do {                                                             \
303         if (PRINT_LEVEL_USER <= kLlErr) {                            \
304             logInfo.EmitLogForUser(num, kLlErr, fmt, ##__VA_ARGS__); \
305         }                                                            \
306     } while (0)
307 
308 #define FATAL(num, fmt, ...)                                           \
309     do {                                                               \
310         if (PRINT_LEVEL_USER <= kLlFatal) {                            \
311             logInfo.EmitLogForUser(num, kLlFatal, fmt, ##__VA_ARGS__); \
312         }                                                              \
313         if (DEBUG_TEST != 0) {                                         \
314             abort();                                                   \
315         } else {                                                       \
316             exit(1);                                                   \
317         }                                                              \
318     } while (0)
319 }  // namespace maple
320 #endif  // MAPLE_UTIL_INCLUDE_MPL_LOGGING_H
321