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 
31 namespace OHOS {
32 namespace HiviewDFX {
33 using namespace std;
34 
35 static size_t g_maxBufferSizeByType[LOG_TYPE_MAX] = {262144, 262144, 262144, 262144, 262144, 0};
36 static int GenerateHilogMsgInside(HilogMsg& hilogMsg, const string& msg, uint16_t logType);
37 
38 // LOG_ONLY_PRERELEASE and LOG_CORE use the same buffer.
ConvertBufType(int type)39 static inline int ConvertBufType(int type)
40 {
41     return type == LOG_ONLY_PRERELEASE ? LOG_CORE : type;
42 }
43 
HilogBuffer(bool isSupportSkipLog)44 HilogBuffer::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 
InitBuffLen()53 void 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 
InitBuffHead()67 void 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 
~HilogBuffer()86 HilogBuffer::~HilogBuffer() {}
87 
Insert(const HilogMsg & msg,bool & isFull)88 size_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
WildcardToRegex(const std::string & wildcard)142 static 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 
LogMatchFilter(const LogFilter & filter,const HilogData & logData)161 static 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 
Query(const LogFilter & filter,const ReaderId & id,int tailCount)223 std::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 
Delete(uint16_t logType)271 int32_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 
CreateBufReader(std::function<void ()> onNewDataCallback)298 HilogBuffer::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 
RemoveBufReader(const ReaderId & id)311 void 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 
IsItemUsed(LogMsgContainer::iterator itemPos)320 bool 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 
OnDeleteItem(LogMsgContainer::iterator itemPos,DeleteReason reason)334 void 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 
OnPushBackedItem(LogMsgContainer & msgList)347 void 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 
OnNewItem(LogMsgContainer & msgList)357 void 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 
GetReader(const ReaderId & id)367 std::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 
GetBuffLen(uint16_t logType)377 int64_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 
SetBuffLen(uint16_t logType,uint64_t buffSize)386 int32_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 
CountLog(const StatsInfo & info)399 void HilogBuffer::CountLog(const StatsInfo &info)
400 {
401     stats.Count(info);
402 }
403 
ResetStats()404 void HilogBuffer::ResetStats()
405 {
406     stats.Reset();
407 }
408 
GetStatsInfo()409 LogStats& HilogBuffer::GetStatsInfo()
410 {
411     return stats;
412 }
413 
GenerateHilogMsgInside(HilogMsg & hilogMsg,const string & msg,uint16_t logType)414 static 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