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