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