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 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 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 #define DBG(tag, fmt, ...) \ 182 do { \ 183 if (PRINT_LEVEL_DEV <= kLlLog) { \ 184 logInfo.EmitLogForDev(tag, kLlLog, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__); \ 185 } \ 186 } while (0) 187 188 // #ifdef LOG 189 // #undef LOG 190 // #endif 191 // #define LOG(tag, fmt, ...) \ 192 // do { \ 193 // if (PRINT_LEVEL_DEV <= kLlLog) { \ 194 // logInfo.EmitLogForDev(tag, kLlLog, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__); \ 195 // } \ 196 // } while (0) 197 198 #ifdef CHECK 199 #undef CHECK 200 #endif 201 #define CHECK(cond, fmt, ...) \ 202 do { \ 203 if (!(cond)) { \ 204 logInfo.EmitErrorMessage(#cond, __FILE__, __LINE__, fmt "\n", ##__VA_ARGS__); \ 205 } \ 206 } while (0) 207 208 #ifdef DCHECK 209 #undef DCHECK 210 #endif 211 #define DCHECK(cond, fmt, ...) \ 212 do { \ 213 DEBUG_STMT(CHECK(cond, fmt, ##__VA_ARGS__)); \ 214 } while (0) 215 216 #define CHECK_FATAL(cond, fmt, ...) \ 217 do { \ 218 if (!(cond)) { \ 219 maple::logInfo.EmitErrorMessage(#cond, __FILE__, __LINE__, fmt "\n", ##__VA_ARGS__); \ 220 if (DEBUG_TEST != 0) { \ 221 abort(); \ 222 } else { \ 223 exit(1); \ 224 } \ 225 } \ 226 } while (0) 227 228 #define CHECK_NULL_FATAL(ptr) CHECK_FATAL((ptr) != nullptr, "Failed with nullptr.") 229 230 #if ENABLE_ASSERT 231 #define DEBUG_ASSERT(cond, fmt, ...) \ 232 do { \ 233 if (!(cond)) { \ 234 maple::logInfo.EmitErrorMessage(#cond, __FILE__, __LINE__, fmt "\n", ##__VA_ARGS__); \ 235 abort(); \ 236 } \ 237 } while (0) 238 239 #define ASSERT_NOT_NULL(ptr) DEBUG_ASSERT((ptr) != nullptr, "Failed with nullptr.") 240 #else 241 #define DEBUG_ASSERT(cond, fmt, ...) 242 #define ASSERT_NOT_NULL(ptr) 243 #endif // ENABLE_ASSERT 244 245 // for user 246 #define PRINT_LEVEL_USER kLlInfo 247 248 #define INFO(num, fmt, ...) \ 249 do { \ 250 if (PRINT_LEVEL_USER <= kLlInfo) { \ 251 logInfo.EmitLogForUser(num, kLlInfo, fmt, ##__VA_ARGS__); \ 252 } \ 253 } while (0) 254 255 #define INFO_V(verbose, num, fmt, ...) \ 256 if (verbose) { \ 257 if (PRINT_LEVEL_USER <= kLlInfo) { \ 258 logInfo.EmitLogForUser(num, kLlInfo, fmt, ##__VA_ARGS__); \ 259 } \ 260 } 261 262 #define WARN(num, fmt, ...) \ 263 do { \ 264 if (PRINT_LEVEL_USER <= kLlWarn) { \ 265 logInfo.EmitLogForUser(num, kLlWarn, fmt, ##__VA_ARGS__); \ 266 } \ 267 } while (0) 268 269 #define ERR(num, fmt, ...) \ 270 do { \ 271 if (PRINT_LEVEL_USER <= kLlErr) { \ 272 logInfo.EmitLogForUser(num, kLlErr, fmt, ##__VA_ARGS__); \ 273 } \ 274 } while (0) 275 276 #define FATAL(num, fmt, ...) \ 277 do { \ 278 if (PRINT_LEVEL_USER <= kLlFatal) { \ 279 logInfo.EmitLogForUser(num, kLlFatal, fmt, ##__VA_ARGS__); \ 280 } \ 281 if (DEBUG_TEST != 0) { \ 282 abort(); \ 283 } else { \ 284 exit(1); \ 285 } \ 286 } while (0) 287 } // namespace maple 288 #endif // MAPLE_UTIL_INCLUDE_MPL_LOGGING_H 289