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 HIPERF_DEBUG_H
17 #define HIPERF_DEBUG_H
18 
19 #include <chrono>
20 #include <cstdio>
21 #include <cstring>
22 #include <map>
23 #include <memory>
24 #include <mutex>
25 #include <sstream>
26 #include <string>
27 #include <unistd.h>
28 
29 #include <gtest/gtest_prod.h>
30 #if !is_mingw
31 #include <sys/syscall.h>
32 #undef gettid
33 #define gettid() syscall(SYS_gettid)
34 #else
35 #include "windows.h"
gettid()36 inline long gettid()
37 {
38     return GetCurrentThreadId();
39 }
40 #endif // !is_mingw
41 
42 namespace OHOS {
43 namespace Developtools {
44 namespace HiPerf {
45 enum DebugLevel {
46     LEVEL_MUCH = 1,
47     LEVEL_VERBOSE,
48     LEVEL_DEBUG,
49     LEVEL_INFO,
50     LEVEL_WARNING,
51     LEVEL_ERROR,
52     LEVEL_FATAL,
53     LEVEL_STDOUT, // printf
54     LEVEL_MAX,    // max
55 };
56 
57 #ifdef HIPERF_DEBUG
58 #if is_ohos || is_double_framework
59 const std::string DEFAULT_UT_LOG_DIR = "/data/local/tmp/";
60 const std::string DEFAULT_LOG_PATH = "/data/local/tmp/hiperf_log.txt";
61 #elif is_mingw
62 const std::string DEFAULT_LOG_PATH = ".\\hiperf_log.txt";
63 #elif is_linux
64 const std::string DEFAULT_UT_LOG_DIR = "./";
65 const std::string DEFAULT_LOG_PATH = "hiperf_log.txt";
66 #else
67 #error unkow os
68 #endif
69 
70 #define HILOG_BASE_TAG "HILOG"
71 #ifndef HILOG_TAG
72 #define HILOG_TAG      ""
73 #define HILOG_TAG_NAME HILOG_BASE_TAG
74 #else
75 #define HILOG_TAG_NAME HILOG_BASE_TAG "_" HILOG_TAG
76 #endif
77 
78 const std::map<DebugLevel, const std::string> DebugLevelMap = {
79     {LEVEL_MUCH, "M"},    {LEVEL_VERBOSE, "V"}, {LEVEL_DEBUG, "D"}, {LEVEL_INFO, "I"},
80     {LEVEL_WARNING, "W"}, {LEVEL_ERROR, "E"},   {LEVEL_FATAL, "F"},
81 };
82 constexpr const int LOG_BUFFER_SIZE = 4 * 1024 * 1024;
83 
84 class DebugLogger {
85 public:
86     DebugLogger();
87     ~DebugLogger();
88 
89     static DebugLogger *GetInstance();
90     DebugLevel SetLogLevel(DebugLevel debugLevel);
91     bool SetMixLogOutput(bool enable);
92     bool SetLogPath(const std::string &logPath);
93     void SetLogTags(const std::string &tags);
94 
95     int Log(DebugLevel level, const std::string &logTag, const char *fmt, ...) const
96         __attribute__((format(printf, 4, 5)));
97     // for class, pointer need add 1 offset (first one is *this)
98 
99     bool EnableHiLog(bool enable = true);
GetLogLevel() const100     DebugLevel GetLogLevel() const
101     {
102         return debugLevel_;
103     };
104 
105     void Disable(bool disable = true);
106     static bool logDisabled_;
107 
108 #ifdef HIPERF_DEBUG_TIME
109     mutable size_t logCount_ = 0;
110     mutable std::chrono::microseconds logTimes_ = std::chrono::microseconds::zero();
111     mutable std::chrono::microseconds logWriteTimes_ = std::chrono::microseconds::zero();
112     mutable std::chrono::microseconds logSprintfTimes_ = std::chrono::microseconds::zero();
113 #endif
114 
115     // used in UT
116     bool OpenLog(const std::string &tempLogPath = "", const std::string &flags = "w");
117     bool RestoreLog();
118     void Reset();
119 
120 private:
121     bool ShouldLog(DebugLevel level, const std::string &logtag) const;
122     DebugLevel GetLogLevelByName(const std::string &) const;
123     DebugLevel GetLogLevelByTag(const std::string &) const;
124     const std::string GetLogLevelName(DebugLevel) const;
125 
126     int HiLog(std::string &buffer) const;
127 
128     static std::unique_ptr<DebugLogger> logInstance_;
129 
130     mutable std::recursive_mutex logMutex_;
131     static DebugLevel debugLevel_;
132     const std::chrono::steady_clock::time_point timeStamp_;
133     FILE *file_ = nullptr;
134     bool mixLogOutput_ = false; // log mix to std
135     bool enableHilog_ = false;
136     bool exitOnFatal_ = true;
137     std::string logPath_;
138     std::map<std::string, DebugLevel> logTagLevelmap_;
139 
140     friend class OptionDebugTest;
141     friend class DebugLoggerTest;
142     FRIEND_TEST(DebugLoggerTest, SetLogTags);
143     FRIEND_TEST(DebugLoggerTest, Disable);
144 };
145 
146 #ifdef HIPERF_DEBUG_PRINTF
147 #ifndef printf
148 #define printf(format, ...)                                                                        \
149     do {                                                                                           \
150         std::printf(format, ##__VA_ARGS__);                                                        \
151         DebugLogger::GetInstance()->Log(LEVEL_STDOUT, HILOG_TAG, format, ##__VA_ARGS__);           \
152     } while (0)
153 #endif
154 
155 #ifndef perror
156 #define perror(format, ...)                                                                        \
157     do {                                                                                           \
158         std::perror(format);                                                                       \
159         DebugLogger::GetInstance()->Log(LEVEL_STDOUT, HILOG_TAG, format "<%d>\n",                  \
160                                         ##__VA_ARGS__, errno);                                     \
161     } while (0)
162 #endif
163 #endif
164 
165 class ScopeDebugLevel {
166 public:
167     explicit ScopeDebugLevel(DebugLevel level, bool mix = false);
168     ~ScopeDebugLevel();
169 
170 private:
171     DebugLevel savedDebugLevel_;
172     bool savedMixOutput_ = false; // log mix to std
173 };
174 
175 #define LOG_LEVEL(LEVEL)  LOG_##LEVEL
176 #define LOG_LEVEL_MUCH    "M:"
177 #define LOG_LEVEL_VERBOSE "V:"
178 #define LOG_LEVEL_DEBUG   "D:"
179 #define LOG_LEVEL_INFO    "I:"
180 #define LOG_LEVEL_WARNING "W:"
181 #define LOG_LEVEL_ERROR   "E:"
182 #define LOG_LEVEL_FATAL   "F:"
183 
184 #ifndef HLOG
185 #ifdef IS_RELEASE_VERSION
186 #define HLOG(level, format, ...)                                                                   \
187     do {                                                                                           \
188         if (__builtin_expect(!DebugLogger::logDisabled_, false)) {                                 \
189             DebugLogger::GetInstance()->Log(                                                       \
190                 level, HILOG_TAG,                                                                  \
191                 HILOG_TAG_NAME "/" LOG_LEVEL(level) "<%ld>%s:" format "\n", gettid(),              \
192                 __FUNCTION__, ##__VA_ARGS__);                                                      \
193         }                                                                                          \
194     } while (0)
195 #else
196 #define HLOG(level, format, ...)                                                                   \
197     do {                                                                                           \
198         if (__builtin_expect(!DebugLogger::logDisabled_, false)) {                                 \
199             DebugLogger::GetInstance()->Log(                                                       \
200                 level, HILOG_TAG,                                                                  \
201                 HILOG_TAG_NAME "/" LOG_LEVEL(level) "<%ld>[%s:%d]%s:" format "\n", gettid(),       \
202                 __FILE_NAME__, __LINE__, __FUNCTION__, ##__VA_ARGS__);                            \
203         }                                                                                          \
204     } while (0)
205 #endif
206 #endif
207 
208 // only log first n times
209 #ifndef HLOGV_FIRST
210 #define HLOGV_FIRST(first, format, ...)                                                            \
211     do {                                                                                           \
212         static int limit = first;                                                                  \
213         if (limit > 0) {                                                                           \
214             HLOG(LEVEL_VERBOSE, format, ##__VA_ARGS__);                                            \
215             if (--limit == 0) {                                                                    \
216                 HLOG(LEVEL_VERBOSE, " nexttime log will be suppressed...");                        \
217             }                                                                                      \
218         }                                                                                          \
219     } while (0)
220 #endif
221 
222 #ifndef HLOGV_FIRST_LOCAL
223 #define HLOGV_FIRST_LOCAL(local_limit, format, ...)                                                \
224     {                                                                                              \
225         if (local_limit != 0) {                                                                    \
226             HLOG(LEVEL_VERBOSE, format, ##__VA_ARGS__);                                            \
227             if (local_limit > 0 && --local_limit == 0) {                                           \
228                 HLOG(LEVEL_VERBOSE, " nexttime log will be suppressed...");                        \
229             }                                                                                      \
230         }                                                                                          \
231     }
232 #endif
233 
234 #ifndef HLOGV
235 #define HLOGV_IF(condition, format, ...)                                                           \
236     if (condition) {                                                                               \
237         HLOG(LEVEL_VERBOSE, format, ##__VA_ARGS__)                                                 \
238     }
239 #define HLOGVVV HLOGV
240 #endif
241 
242 #ifndef HLOGDUMMY
243 #define HLOGDUMMY(format, ...) while (0)
244 #endif
245 
246 #ifndef HLOGM
247 #define HLOGM(format, ...) HLOG(LEVEL_MUCH, format, ##__VA_ARGS__)
248 #define HLOGMMM            HLOGM
249 #endif
250 
251 #ifndef HLOGV
252 #define HLOGV(format, ...) HLOG(LEVEL_VERBOSE, format, ##__VA_ARGS__)
253 #endif
254 
255 #ifndef HLOGD
256 #define HLOGD(format, ...) HLOG(LEVEL_DEBUG, format, ##__VA_ARGS__)
257 #define HLOGDDD            HLOGM
258 #endif
259 
260 #ifndef HLOGI
261 #define HLOGI(format, ...) HLOG(LEVEL_INFO, format, ##__VA_ARGS__)
262 #endif
263 
264 #ifndef HLOGW
265 #define HLOGW(format, ...) HLOG(LEVEL_WARNING, format, ##__VA_ARGS__)
266 #endif
267 
268 #ifndef HLOGE
269 #define HLOGE(format, ...) HLOG(LEVEL_ERROR, format, ##__VA_ARGS__)
270 #endif
271 
272 #ifndef HLOGEP
273 #define HLOGEP(format, ...)                                                                        \
274     HLOG(LEVEL_ERROR, format "(errno %d)", ##__VA_ARGS__, errno)
275 #endif
276 
277 #ifndef HLOGF
278 #ifdef IS_RELEASE_VERSION
279 #define HLOGF(format, ...) HLOG(LEVEL_FATAL, "FATAL error occured, " format, ##__VA_ARGS__)
280 #else
281 #define HLOGF(format, ...)                                                                         \
282     HLOG(LEVEL_FATAL, "FATAL error at %s:%d " format, __FILE_NAME__, __LINE__, ##__VA_ARGS__)
283 #endif
284 #endif
285 
286 #ifndef HLOG_ASSERT_MESSAGE
287 #define HLOG_ASSERT_MESSAGE(condition, format, ...)                                                \
288     if (!(condition)) {                                                                            \
289         HLOG(LEVEL_FATAL, " assert failed: '%s' " format, #condition, ##__VA_ARGS__);              \
290     }
291 #endif
292 
293 #ifndef HLOG_ASSERT
294 #define HLOG_ASSERT(condition) HLOG_ASSERT_MESSAGE(condition, "")
295 #endif
296 
297 #undef assert
298 #else
299 #define HLOGDUMMY(...)                                                                             \
300     do {                                                                                           \
301     } while (0)
302 #define HLOGEP(...)                                                                                \
303     do {                                                                                           \
304     } while (0)
305 #define HLOGM(...)                                                                                 \
306     do {                                                                                           \
307     } while (0)
308 #define HLOGMMM(...)                                                                               \
309     do {                                                                                           \
310     } while (0)
311 #define HLOGV(...)                                                                                 \
312     do {                                                                                           \
313     } while (0)
314 #define HLOGVVV(...)                                                                               \
315     do {                                                                                           \
316     } while (0)
317 #define HLOGD(...)                                                                                 \
318     do {                                                                                           \
319     } while (0)
320 #define HLOGDDD(...)                                                                               \
321     do {                                                                                           \
322     } while (0)
323 #define HLOGI(...)                                                                                 \
324     do {                                                                                           \
325     } while (0)
326 #define HLOGW(...)                                                                                 \
327     do {                                                                                           \
328     } while (0)
329 #define HLOGE(...)                                                                                 \
330     do {                                                                                           \
331     } while (0)
332 #define HLOGF(...)                                                                                 \
333     do {                                                                                           \
334     } while (0)
335 #define HLOG_ASSERT_MESSAGE(...)                                                                   \
336     do {                                                                                           \
337     } while (0)
338 #define HLOG_ASSERT(...)                                                                           \
339     do {                                                                                           \
340     } while (0)
341 
342 class ScopeDebugLevel {
343 public:
ScopeDebugLevel(DebugLevel level, bool mix = false)344     ScopeDebugLevel(DebugLevel level, bool mix = false) {}
345 };
346 #endif
347 } // namespace HiPerf
348 } // namespace Developtools
349 } // namespace OHOS
350 #endif // _HIPERF_DEBUG_H_
351