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 static std::ostream &Info(); 138 static std::ostream &Err(); 139 LogInfo &operator=(const LogInfo &p) = delete; 140 ~LogInfo()141 ~LogInfo() 142 { 143 (void)fclose(outStream); 144 } 145 146 static std::ostream &MapleLogger(LogLevel level = kLlLog); 147 static std::ios::fmtflags Flags(); 148 void EmitLogForUser(enum LogNumberCode num, enum LogLevel ll, const char *fmt, ...) const; 149 void EmitLogForUser(enum LogNumberCode num, enum LogLevel ll, const std::string &message) const; 150 void EmitErrorMessage(const std::string &cond, const std::string &file, unsigned int line, const char *fmt, 151 ...) const; 152 153 private: SetLogDevice(FILE & stream)154 void SetLogDevice(FILE &stream) 155 { 156 outStream = &stream; 157 } SetLogMode(LogMode lm)158 void SetLogMode(LogMode lm) 159 { 160 outMode = lm; 161 } 162 void EmitLogForDevelop(enum LogTags tag, enum LogLevel ll, const std::string &file, const std::string &func, 163 int line, const char *fmt, ...); 164 FILE *outStream; 165 LogMode outMode; 166 }; 167 168 #ifdef DEBUG // no debug in default 169 #define DEBUG_STMT(x) x 170 #define DEBUG_TEST 1 171 #define ENABLE_ASSERT 1 172 #else 173 #define DEBUG_STMT(x) 174 #define DEBUG_TEST 0 175 #define ENABLE_ASSERT 0 176 #endif // DEBUG 177 178 // for developer 179 #define PRINT_LEVEL_DEV kLlLog 180 181 #ifndef IS_RELEASE_VERSION 182 #define DBG(tag, fmt, ...) \ 183 do { \ 184 if (PRINT_LEVEL_DEV <= kLlLog) { \ 185 logInfo.EmitLogForDev(tag, kLlLog, __FILE_NAME__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__); \ 186 } \ 187 } while (0) 188 #else 189 #define DBG(tag, fmt, ...) 190 #endif // IS_RELEASE_VERSION 191 192 #ifdef CHECK 193 #undef CHECK 194 #endif 195 196 #ifndef IS_RELEASE_VERSION 197 #define CHECK(cond, fmt, ...) \ 198 do { \ 199 if (!(cond)) { \ 200 logInfo.EmitErrorMessage(#cond, __FILE_NAME__, __LINE__, fmt "\n", ##__VA_ARGS__); \ 201 } \ 202 } while (0) 203 #else 204 #define CHECK(cond, fmt, ...) \ 205 do { \ 206 if (!(cond)) {} \ 207 } while (0) 208 #endif // IS_RELEASE_VERSION 209 210 #ifdef DCHECK 211 #undef DCHECK 212 #endif 213 #define DCHECK(cond, fmt, ...) \ 214 do { \ 215 DEBUG_STMT(CHECK(cond, fmt, ##__VA_ARGS__)); \ 216 } while (0) 217 218 // To shut down the codecheck warning: boolean condition for 'if' always evaluates to 'true' 219 #ifndef IS_RELEASE_VERSION 220 #define CHECK_FATAL_FALSE(fmt, ...) \ 221 do { \ 222 maple::logInfo.EmitErrorMessage("false", __FILE_NAME__, __LINE__, fmt "\n", ##__VA_ARGS__); \ 223 exit(1); \ 224 } while (0) 225 226 #define CHECK_FATAL(cond, fmt, ...) \ 227 do { \ 228 if (!(cond)) { \ 229 maple::logInfo.EmitErrorMessage(#cond, __FILE_NAME__, __LINE__, fmt "\n", ##__VA_ARGS__); \ 230 if (DEBUG_TEST != 0) { \ 231 abort(); \ 232 } else { \ 233 exit(1); \ 234 } \ 235 } \ 236 } while (0) 237 #else 238 #define CHECK_FATAL_FALSE(fmt, ...) \ 239 do { \ 240 exit(1); \ 241 } while (0) 242 243 #define CHECK_FATAL(cond, fmt, ...) \ 244 do { \ 245 if (!(cond)) { \ 246 if (DEBUG_TEST != 0) { \ 247 abort(); \ 248 } else { \ 249 exit(1); \ 250 } \ 251 } \ 252 } while (0) 253 #endif // IS_RELEASE_VERSION 254 255 #define CHECK_NULL_FATAL(ptr) CHECK_FATAL((ptr) != nullptr, "Failed with nullptr.") 256 257 #if ENABLE_ASSERT // assert not enabled in default 258 #define DEBUG_ASSERT(cond, fmt, ...) \ 259 do { \ 260 if (!(cond)) { \ 261 maple::logInfo.EmitErrorMessage(#cond, __FILE_NAME__, __LINE__, fmt "\n", ##__VA_ARGS__); \ 262 abort(); \ 263 } \ 264 } while (0) 265 266 #define ASSERT_NOT_NULL(ptr) DEBUG_ASSERT((ptr) != nullptr, "Failed with nullptr.") 267 #else 268 #define DEBUG_ASSERT(cond, fmt, ...) 269 #define ASSERT_NOT_NULL(ptr) 270 #endif // ENABLE_ASSERT 271 272 // for user 273 #define PRINT_LEVEL_USER kLlInfo 274 275 #define INFO(num, fmt, ...) \ 276 do { \ 277 if (PRINT_LEVEL_USER <= kLlInfo) { \ 278 logInfo.EmitLogForUser(num, kLlInfo, fmt, ##__VA_ARGS__); \ 279 } \ 280 } while (0) 281 282 #define INFO_V(verbose, num, fmt, ...) \ 283 if (verbose) { \ 284 if (PRINT_LEVEL_USER <= kLlInfo) { \ 285 logInfo.EmitLogForUser(num, kLlInfo, fmt, ##__VA_ARGS__); \ 286 } \ 287 } 288 289 #define WARN(num, fmt, ...) \ 290 do { \ 291 if (PRINT_LEVEL_USER <= kLlWarn) { \ 292 logInfo.EmitLogForUser(num, kLlWarn, fmt, ##__VA_ARGS__); \ 293 } \ 294 } while (0) 295 296 #define ERR(num, fmt, ...) \ 297 do { \ 298 if (PRINT_LEVEL_USER <= kLlErr) { \ 299 logInfo.EmitLogForUser(num, kLlErr, fmt, ##__VA_ARGS__); \ 300 } \ 301 } while (0) 302 303 #define FATAL(num, fmt, ...) \ 304 do { \ 305 if (PRINT_LEVEL_USER <= kLlFatal) { \ 306 logInfo.EmitLogForUser(num, kLlFatal, fmt, ##__VA_ARGS__); \ 307 } \ 308 if (DEBUG_TEST != 0) { \ 309 abort(); \ 310 } else { \ 311 exit(1); \ 312 } \ 313 } while (0) 314 } // namespace maple 315 #endif // MAPLE_UTIL_INCLUDE_MPL_LOGGING_H 316