1/*
2 * Copyright (c) 2021 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#include <cstring>
17#include <thread>
18#include <vector>
19#include <sys/time.h>
20#include <regex>
21#include <string>
22
23#include <hilog_common.h>
24#include <flow_control.h>
25#include <log_timestamp.h>
26#include <properties.h>
27#include <log_utils.h>
28
29#include "log_buffer.h"
30
31namespace OHOS {
32namespace HiviewDFX {
33using namespace std;
34
35static size_t g_maxBufferSizeByType[LOG_TYPE_MAX] = {262144, 262144, 262144, 262144, 262144, 0};
36static int GenerateHilogMsgInside(HilogMsg& hilogMsg, const string& msg, uint16_t logType);
37
38// LOG_ONLY_PRERELEASE and LOG_CORE use the same buffer.
39static inline int ConvertBufType(int type)
40{
41    return type == LOG_ONLY_PRERELEASE ? LOG_CORE : type;
42}
43
44HilogBuffer::HilogBuffer(bool isSupportSkipLog) : m_isSupportSkipLog(isSupportSkipLog)
45{
46    for (int i = 0; i < LOG_TYPE_MAX; i++) {
47        sizeByType[i] = 0;
48    }
49    InitBuffLen();
50    InitBuffHead();
51}
52
53void HilogBuffer::InitBuffLen()
54{
55    size_t global_size = GetBufferSize(LOG_TYPE_MAX, false);
56    size_t persist_global_size = GetBufferSize(LOG_TYPE_MAX, true);
57    for (int i = 0; i < LOG_TYPE_MAX; i++) {
58        size_t size = GetBufferSize(i, false);
59        size_t persist_size = GetBufferSize(i, true);
60        SetBuffLen(i, global_size);
61        SetBuffLen(i, persist_global_size);
62        SetBuffLen(i, size);
63        SetBuffLen(i, persist_size);
64    }
65}
66
67void HilogBuffer::InitBuffHead()
68{
69    const string msg = "========Zeroth log of type: ";
70    std::vector<char> buf(MAX_LOG_LEN, 0);
71    HilogMsg *headMsg = reinterpret_cast<HilogMsg *>(buf.data());
72
73    for (uint16_t i = 0; i < LOG_TYPE_MAX; i++) {
74        string typeStr = LogType2Str(i);
75        if (typeStr == "invalid") {
76            continue;
77        }
78        string tmpStr = msg + typeStr;
79        if (GenerateHilogMsgInside(*headMsg, tmpStr, i) == RET_SUCCESS) {
80            bool isFull = false;
81            Insert(*headMsg, isFull);
82        }
83    }
84}
85
86HilogBuffer::~HilogBuffer() {}
87
88size_t HilogBuffer::Insert(const HilogMsg& msg, bool& isFull)
89{
90    size_t elemSize = CONTENT_LEN((&msg)); /* include '\0' */
91    if (unlikely(msg.tagLen > MAX_TAG_LEN || msg.tagLen == 0 || elemSize > MAX_LOG_LEN ||
92        elemSize == 0 || msg.type >= LOG_TYPE_MAX)) {
93        return 0;
94    }
95    isFull = false;
96    {
97        std::lock_guard<decltype(hilogBufferMutex)> lock(hilogBufferMutex);
98        int bufferType = ConvertBufType(msg.type);
99
100        // Delete old entries when full
101        if (elemSize + sizeByType[bufferType] >= g_maxBufferSizeByType[bufferType]) {
102            // Drop 5% of maximum log when full
103            std::list<HilogData>::iterator it = hilogDataList.begin();
104            static const float DROP_RATIO = 0.05;
105            while (sizeByType[bufferType] > g_maxBufferSizeByType[bufferType] * (1 - DROP_RATIO) &&
106                it != hilogDataList.end()) {
107                if (ConvertBufType((*it).type) != bufferType) {    // Only remove old logs of the same type
108                    ++it;
109                    continue;
110                }
111
112                if (IsItemUsed(it)) {
113                    isFull = true;
114                    return 0;
115                }
116
117                OnDeleteItem(it, DeleteReason::BUFF_OVERFLOW);
118                size_t cLen = it->len - it->tagLen;
119                sizeByType[ConvertBufType((*it).type)] -= cLen;
120                it = hilogDataList.erase(it);
121            }
122
123            // Re-confirm if enough elements has been removed
124            if (sizeByType[bufferType] >= g_maxBufferSizeByType[bufferType]) {
125                std::cout << "Failed to clean old logs." << std::endl;
126            }
127        }
128
129        // Append new log into HilogBuffer
130        hilogDataList.emplace_back(msg);
131        // Update current size of HilogBuffer
132        sizeByType[bufferType] += elemSize;
133        OnPushBackedItem(hilogDataList);
134    }
135
136    // Notify readers about new element added
137    OnNewItem(hilogDataList);
138    return elemSize;
139}
140
141// Replace wildcard with regex
142static std::string WildcardToRegex(const std::string& wildcard)
143{
144    // Original and Replacement char array
145    const static char* WILDCARDS = "*?[]+.^&";
146    const static std::string REPLACEMENT_S[] = {".*", ".", "\\[", "\\]", "\\+", "\\.", "\\^", "\\&"};
147    // Modify every wildcard to regex
148    std::string result = "";
149    for (char c : wildcard) {
150        // strchr matches wildcard and char
151        if (std::strchr(WILDCARDS, c) != nullptr) {
152            size_t index = std::strchr(WILDCARDS, c) - WILDCARDS;
153            result += REPLACEMENT_S[index];
154        } else {
155            result += c;
156        }
157    }
158    return result;
159}
160
161static bool LogMatchFilter(const LogFilter& filter, const HilogData& logData)
162{
163    // types & levels match
164    if (((static_cast<uint16_t>(0b01 << logData.type)) & filter.types) == 0) {
165        return false;
166    }
167    if (((static_cast<uint16_t>(0b01 << logData.level)) & filter.levels) == 0) {
168        return false;
169    }
170
171    int i = 0;
172    // domain match
173    static constexpr uint32_t LOW_BYTE = 0xFF;
174    static constexpr uint32_t LOW_BYTE_REVERSE = ~LOW_BYTE;
175    /* 1) domain id equals exactly: (0xd012345 == 0xd012345)
176       2) last 8 bits is sub domain id, if it's 0xFF, compare high 24 bits:
177       (0xd0123ff & 0xffffff00 == 0xd012345 & 0xffffff00) */
178    bool match = false;
179    for (i = 0; i < filter.domainCount; i++) {
180        if ((logData.domain == filter.domains[i]) || ((static_cast<uint8_t>(filter.domains[i]) == LOW_BYTE)
181             && ((logData.domain & LOW_BYTE_REVERSE) == (filter.domains[i] & LOW_BYTE_REVERSE)))) {
182            match = true;
183            break;
184        }
185    }
186    if (filter.domainCount && match == filter.blackDomain) {
187        return false;
188    }
189    match = false;
190    // tag match
191    for (i = 0; i < filter.tagCount; i++) {
192        if (strcmp(logData.tag, filter.tags[i]) == 0) {
193            match = true;
194            break;
195        }
196    }
197    if (filter.tagCount && match == filter.blackTag) {
198        return false;
199    }
200    match = false;
201    // pid match
202    for (i = 0; i < filter.pidCount; i++) {
203        if (logData.pid == filter.pids[i]) {
204            match = true;
205            break;
206        }
207    }
208    if (filter.pidCount && match == filter.blackPid) {
209        return false;
210    }
211    // regular expression match
212    if (filter.regex[0] != 0) {
213        // Added a WildcardToRegex function for invalid regex.
214        std::string wildcardRegex = WildcardToRegex(filter.regex);
215        std::regex regExpress(wildcardRegex);
216        if (std::regex_search(logData.content, regExpress) == false) {
217            return false;
218        }
219    }
220    return true;
221}
222
223std::optional<HilogData> HilogBuffer::Query(const LogFilter& filter, const ReaderId& id, int tailCount)
224{
225    auto reader = GetReader(id);
226    if (!reader) {
227        std::cerr << "Reader not registered!\n";
228        return std::nullopt;
229    }
230
231    std::shared_lock<decltype(hilogBufferMutex)> lock(hilogBufferMutex);
232
233    if (reader->m_msgList != &hilogDataList) {
234        reader->m_msgList = &hilogDataList;
235        if (tailCount == 0) {
236            reader->m_pos = hilogDataList.begin();
237        } else {
238            reader->m_pos = hilogDataList.end();
239            reader->m_pos--;
240        }
241        for (int i = 0; (i < tailCount) && (reader->m_pos != hilogDataList.begin());) {
242            if (LogMatchFilter(filter, (*reader->m_pos))) {
243                i++;
244            }
245            reader->m_pos--;
246        }
247    }
248
249    if (reader->skipped) {
250        const string msg = "========Slow reader missed log lines: ";
251        const string tmpStr = msg + to_string(reader->skipped);
252        std::vector<char> buf(MAX_LOG_LEN, 0);
253        HilogMsg *headMsg = reinterpret_cast<HilogMsg *>(buf.data());
254        if (GenerateHilogMsgInside(*headMsg, tmpStr, LOG_CORE) == RET_SUCCESS) {
255            const HilogData logData(*headMsg);
256            reader->skipped = 0;
257            return logData;
258        }
259    }
260
261    while (reader->m_pos != hilogDataList.end()) {
262        const HilogData& logData = *reader->m_pos;
263        reader->m_pos++;
264        if (LogMatchFilter(filter, logData)) {
265            return logData;
266        }
267    }
268    return std::nullopt;
269}
270
271int32_t HilogBuffer::Delete(uint16_t logType)
272{
273    if (logType >= LOG_TYPE_MAX) {
274        return ERR_LOG_TYPE_INVALID;
275    }
276    size_t sum = 0;
277    std::unique_lock<decltype(hilogBufferMutex)> lock(hilogBufferMutex);
278    std::list<HilogData>::iterator it = hilogDataList.begin();
279
280    // Delete logs corresponding to queryCondition
281    while (it != hilogDataList.end()) {
282        // Only remove old logs of the same type
283        if ((*it).type != logType) {
284            ++it;
285            continue;
286        }
287        // Delete corresponding logs
288        OnDeleteItem(it, DeleteReason::CMD_CLEAR);
289
290        size_t cLen = it->len - it->tagLen;
291        sum += cLen;
292        sizeByType[(*it).type] -= cLen;
293        it = hilogDataList.erase(it);
294    }
295    return sum;
296}
297
298HilogBuffer::ReaderId HilogBuffer::CreateBufReader(std::function<void()> onNewDataCallback)
299{
300    std::unique_lock<decltype(m_logReaderMtx)> lock(m_logReaderMtx);
301    auto reader = std::make_shared<BufferReader>();
302    if (reader != nullptr) {
303        reader->skipped = 0;
304        reader->m_onNewDataCallback = onNewDataCallback;
305    }
306    ReaderId id = reinterpret_cast<ReaderId>(reader.get());
307    m_logReaders.insert(std::make_pair(id, reader));
308    return id;
309}
310
311void HilogBuffer::RemoveBufReader(const ReaderId& id)
312{
313    std::unique_lock<decltype(m_logReaderMtx)> lock(m_logReaderMtx);
314    auto it = m_logReaders.find(id);
315    if (it != m_logReaders.end()) {
316        m_logReaders.erase(it);
317    }
318}
319
320bool HilogBuffer::IsItemUsed(LogMsgContainer::iterator itemPos)
321{
322    if (m_isSupportSkipLog) {
323        return false;
324    }
325    std::shared_lock<decltype(m_logReaderMtx)> lock(m_logReaderMtx);
326    for (auto& [id, readerPtr] : m_logReaders) {
327        if (readerPtr->m_pos == itemPos) {
328            return true;
329        }
330    }
331    return false;
332}
333
334void HilogBuffer::OnDeleteItem(LogMsgContainer::iterator itemPos, DeleteReason reason)
335{
336    std::shared_lock<decltype(m_logReaderMtx)> lock(m_logReaderMtx);
337    for (auto& [id, readerPtr] : m_logReaders) {
338        if (readerPtr->m_pos == itemPos) {
339            readerPtr->m_pos = std::next(itemPos);
340            if (reason == DeleteReason::BUFF_OVERFLOW) {
341                readerPtr->skipped++;
342            }
343        }
344    }
345}
346
347void HilogBuffer::OnPushBackedItem(LogMsgContainer& msgList)
348{
349    std::shared_lock<decltype(m_logReaderMtx)> lock(m_logReaderMtx);
350    for (auto& [id, readerPtr] : m_logReaders) {
351        if (readerPtr->m_pos == msgList.end()) {
352            readerPtr->m_pos = std::prev(msgList.end());
353        }
354    }
355}
356
357void HilogBuffer::OnNewItem(LogMsgContainer& msgList)
358{
359    std::shared_lock<decltype(m_logReaderMtx)> lock(m_logReaderMtx);
360    for (auto& [id, readerPtr] : m_logReaders) {
361        if (readerPtr->m_msgList == &msgList && readerPtr->m_onNewDataCallback) {
362            readerPtr->m_onNewDataCallback();
363        }
364    }
365}
366
367std::shared_ptr<HilogBuffer::BufferReader> HilogBuffer::GetReader(const ReaderId& id)
368{
369    std::shared_lock<decltype(m_logReaderMtx)> lock(m_logReaderMtx);
370    auto it = m_logReaders.find(id);
371    if (it != m_logReaders.end()) {
372        return it->second;
373    }
374    return std::shared_ptr<HilogBuffer::BufferReader>();
375}
376
377int64_t HilogBuffer::GetBuffLen(uint16_t logType)
378{
379    if (logType >= LOG_TYPE_MAX) {
380        return ERR_LOG_TYPE_INVALID;
381    }
382    uint64_t buffSize = g_maxBufferSizeByType[logType];
383    return buffSize;
384}
385
386int32_t HilogBuffer::SetBuffLen(uint16_t logType, uint64_t buffSize)
387{
388    if (logType >= LOG_TYPE_MAX) {
389        return ERR_LOG_TYPE_INVALID;
390    }
391    if (buffSize < MIN_BUFFER_SIZE || buffSize > MAX_BUFFER_SIZE) {
392        return ERR_BUFF_SIZE_INVALID;
393    }
394    std::unique_lock<decltype(hilogBufferMutex)> lock(hilogBufferMutex);
395    g_maxBufferSizeByType[logType] = buffSize;
396    return RET_SUCCESS;
397}
398
399void HilogBuffer::CountLog(const StatsInfo &info)
400{
401    stats.Count(info);
402}
403
404void HilogBuffer::ResetStats()
405{
406    stats.Reset();
407}
408
409LogStats& HilogBuffer::GetStatsInfo()
410{
411    return stats;
412}
413
414static int GenerateHilogMsgInside(HilogMsg& hilogMsg, const string& msg, uint16_t logType)
415{
416    const string tag = "HiLog";
417    size_t contentLen =  tag.length() + 1 + msg.length() + 1;
418    hilogMsg.len = static_cast<uint16_t>(sizeof(HilogMsg) + contentLen);
419    hilogMsg.len = hilogMsg.len > MAX_LOG_LEN ? MAX_LOG_LEN : hilogMsg.len;
420    contentLen = hilogMsg.len - static_cast<uint16_t>(sizeof(HilogMsg));
421
422    struct timespec ts = {0};
423    (void)clock_gettime(CLOCK_REALTIME, &ts);
424    struct timespec ts_mono = {0};
425    (void)clock_gettime(CLOCK_MONOTONIC, &ts_mono);
426    hilogMsg.tv_sec = static_cast<uint32_t>(ts.tv_sec);
427    hilogMsg.tv_nsec = static_cast<uint32_t>(ts.tv_nsec);
428    hilogMsg.mono_sec = static_cast<uint32_t>(ts_mono.tv_nsec);
429    hilogMsg.type = logType;
430    hilogMsg.level = LOG_INFO;
431    hilogMsg.pid = 0;
432    hilogMsg.tid = 0;
433    hilogMsg.domain = 0;
434    hilogMsg.tagLen = tag.length() + 1;
435    if (memcpy_s(hilogMsg.tag, contentLen, tag.c_str(), hilogMsg.tagLen) != 0) {
436        return RET_FAIL;
437    }
438    if (memcpy_s(hilogMsg.tag + hilogMsg.tagLen, contentLen - hilogMsg.tagLen, msg.c_str(), msg.length() + 1) != 0) {
439        return RET_FAIL;
440    }
441
442    return RET_SUCCESS;
443}
444} // namespace HiviewDFX
445} // namespace OHOS
446