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"
36inline long gettid()
37{
38    return GetCurrentThreadId();
39}
40#endif // !is_mingw
41
42namespace OHOS {
43namespace Developtools {
44namespace HiPerf {
45enum 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
59const std::string DEFAULT_UT_LOG_DIR = "/data/local/tmp/";
60const std::string DEFAULT_LOG_PATH = "/data/local/tmp/hiperf_log.txt";
61#elif is_mingw
62const std::string DEFAULT_LOG_PATH = ".\\hiperf_log.txt";
63#elif is_linux
64const std::string DEFAULT_UT_LOG_DIR = "./";
65const 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
78const 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};
82constexpr const int LOG_BUFFER_SIZE = 4 * 1024 * 1024;
83
84class DebugLogger {
85public:
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);
100    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
120private:
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
165class ScopeDebugLevel {
166public:
167    explicit ScopeDebugLevel(DebugLevel level, bool mix = false);
168    ~ScopeDebugLevel();
169
170private:
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
342class ScopeDebugLevel {
343public:
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