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