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