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 "log_persister.h"
17
18 #include <sys/mman.h>
19 #include <sys/stat.h>
20 #include <sys/prctl.h>
21
22 #include <fcntl.h>
23 #include <securec.h>
24
25 #include <algorithm>
26 #include <array>
27 #include <chrono>
28 #include <climits>
29 #include <cstdio>
30 #include <cstdlib>
31 #include <cstring>
32 #include <iostream>
33 #include <mutex>
34 #include <sstream>
35 #include <string>
36 #include <thread>
37 #include <unistd.h>
38
39 #include "hilog_common.h"
40 #include "log_buffer.h"
41 #include "log_compress.h"
42 #include "format.h"
43
44 namespace OHOS {
45 namespace HiviewDFX {
46
47 static constexpr int DEFAULT_LOG_LEVEL = 1<<LOG_DEBUG | 1<<LOG_INFO | 1<<LOG_WARN | 1 <<LOG_ERROR | 1 <<LOG_FATAL;
48 static constexpr int SLEEP_TIME = 5;
49
isEmptyThread(const std::thread & th)50 static bool isEmptyThread(const std::thread& th)
51 {
52 static const std::thread EMPTY_THREAD;
53 return th.get_id() == EMPTY_THREAD.get_id();
54 }
55
56 std::recursive_mutex LogPersister::s_logPersistersMtx;
57 std::list<std::shared_ptr<LogPersister>> LogPersister::s_logPersisters;
58
CreateLogPersister(HilogBuffer & buffer)59 std::shared_ptr<LogPersister> LogPersister::CreateLogPersister(HilogBuffer &buffer)
60 {
61 // Because of:
62 // - static_assert(is_constructible<_Tp, _Args...>::value, "Can't construct object in make_shared");
63 // make shared can't be used!
64 return std::shared_ptr<LogPersister>(new LogPersister(buffer));
65 }
66
LogPersister(HilogBuffer & buffer)67 LogPersister::LogPersister(HilogBuffer &buffer) : m_hilogBuffer(buffer)
68 {
69 m_mappedPlainLogFile = nullptr;
70 m_bufReader = m_hilogBuffer.CreateBufReader([this]() { NotifyNewLogAvailable(); });
71 }
72
~LogPersister()73 LogPersister::~LogPersister()
74 {
75 m_hilogBuffer.RemoveBufReader(m_bufReader);
76 Deinit();
77 }
78
InitCompression()79 int LogPersister::InitCompression()
80 {
81 m_compressBuffer = std::make_unique<LogPersisterBuffer>();
82 if (!m_compressBuffer) {
83 return RET_FAIL;
84 }
85 switch (m_baseData.compressAlg) {
86 case COMPRESS_TYPE_NONE:
87 m_compressor = std::make_unique<NoneCompress>();
88 break;
89 case COMPRESS_TYPE_ZLIB:
90 m_compressor = std::make_unique<ZlibCompress>();
91 break;
92 case COMPRESS_TYPE_ZSTD:
93 m_compressor = std::make_unique<ZstdCompress>();
94 break;
95 default:
96 break;
97 }
98 if (!m_compressor) {
99 return RET_FAIL;
100 }
101 return RET_SUCCESS;
102 }
103
InitFileRotator(const InitData & initData)104 int LogPersister::InitFileRotator(const InitData& initData)
105 {
106 std::string fileSuffix = "";
107 switch (m_baseData.compressAlg) {
108 case CompressAlg::COMPRESS_TYPE_ZSTD:
109 fileSuffix = ".zst";
110 break;
111 case CompressAlg::COMPRESS_TYPE_ZLIB:
112 fileSuffix = ".gz";
113 break;
114 default:
115 break;
116 };
117 m_fileRotator = std::make_unique<LogPersisterRotator>(m_baseData.logPath, m_baseData.id,
118 m_baseData.maxLogFileNum, fileSuffix);
119 if (!m_fileRotator) {
120 std::cerr << "Not enough memory!\n";
121 return RET_FAIL;
122 }
123
124 PersistRecoveryInfo info = {0};
125 bool restore = false;
126 if (std::holds_alternative<LogPersistStartMsg>(initData)) {
127 info.msg = std::get<LogPersistStartMsg>(initData);
128 info.types = m_filters.inclusions.types;
129 info.levels = m_filters.inclusions.levels;
130 } else if (std::holds_alternative<PersistRecoveryInfo>(initData)) {
131 info = std::get<PersistRecoveryInfo>(initData);
132 restore = true;
133 }
134 return m_fileRotator->Init(info, restore);
135 }
136
Init(const InitData & initData)137 int LogPersister::Init(const InitData& initData)
138 {
139 std::cout << __PRETTY_FUNCTION__ << " Begin\n";
140 std::lock_guard<decltype(m_initMtx)> lock(m_initMtx);
141 if (m_inited) {
142 return 0;
143 }
144
145 auto initByMsg = [this](const LogPersistStartMsg& msg) {
146 m_baseData.id = msg.jobId;
147 m_baseData.logPath = msg.filePath;
148 m_baseData.logFileSizeLimit = msg.fileSize;
149 m_baseData.maxLogFileNum = msg.fileNum;
150 m_baseData.compressAlg = msg.compressAlg;
151 m_baseData.newLogTimeout = std::chrono::seconds(SLEEP_TIME);
152
153 m_filters.inclusions.types = msg.logType;
154 m_filters.inclusions.levels = DEFAULT_LOG_LEVEL;
155 };
156
157 bool restore = false;
158 if (std::holds_alternative<LogPersistStartMsg>(initData)) {
159 const LogPersistStartMsg& msg = std::get<LogPersistStartMsg>(initData);
160 initByMsg(msg);
161 } else if (std::holds_alternative<PersistRecoveryInfo>(initData)) {
162 const LogPersistStartMsg& msg = std::get<PersistRecoveryInfo>(initData).msg;
163 initByMsg(msg);
164 restore = true;
165 } else {
166 std::cerr << __PRETTY_FUNCTION__ << "Init data not provided\n";
167 return RET_FAIL;
168 }
169
170 size_t separatorPos = m_baseData.logPath.find_last_of('/');
171 if (separatorPos == std::string::npos) {
172 return ERR_LOG_PERSIST_FILE_PATH_INVALID;
173 }
174
175 std::string parentPath = m_baseData.logPath.substr(0, separatorPos);
176 if (access(parentPath.c_str(), F_OK) != 0) {
177 perror("persister directory does not exist.");
178 return ERR_LOG_PERSIST_FILE_PATH_INVALID;
179 }
180
181 // below guard is needed to have sure only one Path and Id is reqistered till end of init!
182 std::lock_guard<decltype(s_logPersistersMtx)> guard(s_logPersistersMtx);
183 if (CheckRegistered(m_baseData.id, m_baseData.logPath)) {
184 std::cerr << __PRETTY_FUNCTION__ << "Log persister already registered. Path:" << m_baseData.logPath
185 << " id:" << m_baseData.id << "\n";
186 return ERR_LOG_PERSIST_TASK_FAIL;
187 }
188 if (InitCompression() != RET_SUCCESS) {
189 return ERR_LOG_PERSIST_COMPRESS_INIT_FAIL;
190 }
191 if (int result = InitFileRotator(initData); result != RET_SUCCESS) {
192 return result;
193 }
194
195 if (int result = PrepareUncompressedFile(parentPath, restore)) {
196 return result;
197 }
198
199 RegisterLogPersister(shared_from_this());
200 m_inited = true;
201 std::cout << __PRETTY_FUNCTION__ << " Done\n";
202 return 0;
203 }
204
Deinit()205 int LogPersister::Deinit()
206 {
207 std::cout << __PRETTY_FUNCTION__ << " Begin\n";
208 std::lock_guard<decltype(m_initMtx)> lock(m_initMtx);
209 if (!m_inited) {
210 return 0;
211 }
212
213 Stop();
214
215 munmap(m_mappedPlainLogFile, MAX_PERSISTER_BUFFER_SIZE);
216 std::cout << "Removing unmapped plain log file: " << m_plainLogFilePath << "\n";
217 if (remove(m_plainLogFilePath.c_str())) {
218 std::cerr << "File: " << m_plainLogFilePath << " can't be removed. ";
219 HilogPrintError(errno);
220 }
221
222 DeregisterLogPersister(shared_from_this());
223 m_inited = false;
224 std::cout << __PRETTY_FUNCTION__ << " Done\n";
225 return 0;
226 }
227
PrepareUncompressedFile(const std::string & parentPath,bool restore)228 int LogPersister::PrepareUncompressedFile(const std::string& parentPath, bool restore)
229 {
230 std::string fileName = std::string(".") + AUXILLARY_PERSISTER_PREFIX + std::to_string(m_baseData.id);
231 m_plainLogFilePath = parentPath + "/" + fileName;
232 FILE* plainTextFile = fopen(m_plainLogFilePath.c_str(), restore ? "r+" : "w+");
233
234 if (!plainTextFile) {
235 std::cerr << __PRETTY_FUNCTION__ << " Open uncompressed log file(" << m_plainLogFilePath << ") failed: ";
236 HilogPrintError(errno);
237 return ERR_LOG_PERSIST_FILE_OPEN_FAIL;
238 }
239
240 if (!restore) {
241 ftruncate(fileno(plainTextFile), sizeof(LogPersisterBuffer));
242 fflush(plainTextFile);
243 fsync(fileno(plainTextFile));
244 }
245 m_mappedPlainLogFile = (LogPersisterBuffer *)mmap(nullptr, sizeof(LogPersisterBuffer), PROT_READ | PROT_WRITE,
246 MAP_SHARED, fileno(plainTextFile), 0);
247 if (fclose(plainTextFile)) {
248 std::cerr << "File: " << plainTextFile << " can't be closed. ";
249 HilogPrintError(errno);
250 }
251 if (m_mappedPlainLogFile == MAP_FAILED) {
252 std::cerr << __PRETTY_FUNCTION__ << " mmap file failed: ";
253 HilogPrintError(errno);
254 return RET_FAIL;
255 }
256 if (restore == true) {
257 #ifdef DEBUG
258 std::cout << __PRETTY_FUNCTION__ << " Recovered persister, Offset=" << m_mappedPlainLogFile->offset << "\n";
259 #endif
260 // try to store previous uncompressed logs
261 auto compressionResult = m_compressor->Compress(*m_mappedPlainLogFile, *m_compressBuffer);
262 if (compressionResult != 0) {
263 std::cerr << __PRETTY_FUNCTION__ << " Compression error. Result:" << compressionResult << "\n";
264 return RET_FAIL;
265 };
266 WriteCompressedLogs();
267 } else {
268 m_mappedPlainLogFile->offset = 0;
269 }
270 return 0;
271 }
272
NotifyNewLogAvailable()273 void LogPersister::NotifyNewLogAvailable()
274 {
275 m_receiveLogCv.notify_one();
276 }
277
LogDataToFormatedStrings(const HilogData & logData)278 std::list<std::string> LogDataToFormatedStrings(const HilogData& logData)
279 {
280 std::list<std::string> resultLogLines;
281 std::array<char, MAX_LOG_LEN*2> tempBuffer = {0};
282 HilogShowFormatBuffer showBuffer;
283 showBuffer.level = logData.level;
284 showBuffer.pid = logData.pid;
285 showBuffer.tid = logData.tid;
286 showBuffer.domain = logData.domain;
287 showBuffer.tv_sec = logData.tv_sec;
288 showBuffer.tv_nsec = logData.tv_nsec;
289
290 std::vector<char> dataCopy(logData.len, 0);
291 if (dataCopy.data() == nullptr) {
292 return resultLogLines;
293 }
294 if (memcpy_s(dataCopy.data(), logData.len, logData.tag, logData.len)) {
295 return resultLogLines;
296 }
297 showBuffer.data = dataCopy.data();
298 // Below code replace 'new line' character with 'zero' to simulate
299 // continuation of very long log message with tag prefix at the begining of every line.
300 // e.g. "This is very \n long line \n for sure!!!"
301 // This will be changed into:
302 // <formated meta data> <tag info> This is very
303 // <formated meta data> <tag info> long line
304 // <formated meta data> <tag info> for sure!!!
305 size_t newLineOffset = logData.tag_len;
306 char *msgBegin = dataCopy.data() + newLineOffset;
307 char *currenMsgPos = msgBegin;
308 while (*currenMsgPos != 0) {
309 if (*currenMsgPos == '\n') {
310 if (currenMsgPos != msgBegin) {
311 *currenMsgPos = 0;
312 showBuffer.tag_len = newLineOffset;
313 HilogShowBuffer(tempBuffer.data(), tempBuffer.size(), showBuffer, OFF_SHOWFORMAT);
314 resultLogLines.push_back(tempBuffer.data());
315 newLineOffset += currenMsgPos - msgBegin + 1;
316 } else {
317 newLineOffset++;
318 }
319 msgBegin = currenMsgPos + 1;
320 }
321 currenMsgPos++;
322 }
323 if (currenMsgPos != msgBegin) {
324 showBuffer.tag_len = newLineOffset;
325 HilogShowBuffer(tempBuffer.data(), tempBuffer.size(), showBuffer, OFF_SHOWFORMAT);
326 resultLogLines.push_back(tempBuffer.data());
327 }
328 return resultLogLines;
329 }
330
WriteUncompressedLogs(std::list<std::string> & formatedTextLogs)331 bool LogPersister::WriteUncompressedLogs(std::list<std::string>& formatedTextLogs)
332 {
333 while (!formatedTextLogs.empty()) {
334 std::string logLine = formatedTextLogs.front();
335 uint16_t size = logLine.length() + 1; // we want to add new line character
336 uint32_t origOffset = m_mappedPlainLogFile->offset;
337 if (m_mappedPlainLogFile->offset + size > MAX_PERSISTER_BUFFER_SIZE)
338 return false;
339
340 char* currentContentPos = m_mappedPlainLogFile->content + m_mappedPlainLogFile->offset;
341 uint32_t remainingSpace = MAX_PERSISTER_BUFFER_SIZE - m_mappedPlainLogFile->offset;
342 int r = memcpy_s(currentContentPos, remainingSpace, logLine.c_str(), logLine.length());
343 if (r != 0) {
344 std::cerr << __PRETTY_FUNCTION__ << " Can't copy part of memory!\n";
345 m_mappedPlainLogFile->offset = origOffset;
346 return true;
347 }
348 formatedTextLogs.pop_front();
349 m_mappedPlainLogFile->offset += logLine.length();
350 m_mappedPlainLogFile->content[m_mappedPlainLogFile->offset] = '\n';
351 m_mappedPlainLogFile->offset += 1;
352 }
353 return true;
354 }
355
WriteLogData(const HilogData & logData)356 int LogPersister::WriteLogData(const HilogData& logData)
357 {
358 std::list<std::string> formatedTextLogs = LogDataToFormatedStrings(logData);
359
360 // Firstly gather uncompressed logs in auxiliary file
361 if (WriteUncompressedLogs(formatedTextLogs))
362 return 0;
363 // Try to compress auxiliary file
364 auto compressionResult = m_compressor->Compress(*m_mappedPlainLogFile, *m_compressBuffer);
365 if (compressionResult != 0) {
366 std::cerr << __PRETTY_FUNCTION__ << " Compression error. Result:" << compressionResult << "\n";
367 return RET_FAIL;
368 };
369 // Write compressed buffor and clear counters
370 WriteCompressedLogs();
371 // Try again write data that wasn't written at the beginning
372 // If again fail then these logs are skipped
373 return WriteUncompressedLogs(formatedTextLogs) ? 0 : RET_FAIL;
374 }
375
WriteCompressedLogs()376 inline void LogPersister::WriteCompressedLogs()
377 {
378 if (m_mappedPlainLogFile->offset == 0)
379 return;
380 m_fileRotator->Input(m_compressBuffer->content, m_compressBuffer->offset);
381 m_plainLogSize += m_mappedPlainLogFile->offset;
382 std::cout << __PRETTY_FUNCTION__ << " Stored plain log bytes: " << m_plainLogSize
383 << " from: " << m_baseData.logFileSizeLimit << "\n";
384 if (m_plainLogSize >= m_baseData.logFileSizeLimit) {
385 m_plainLogSize = 0;
386 m_fileRotator->FinishInput();
387 }
388 m_compressBuffer->offset = 0;
389 m_mappedPlainLogFile->offset = 0;
390 }
391
Start()392 void LogPersister::Start()
393 {
394 {
395 std::lock_guard<decltype(m_initMtx)> lock(m_initMtx);
396 if (!m_inited) {
397 std::cerr << __PRETTY_FUNCTION__ << " Log persister wasn't inited!\n";
398 return;
399 }
400 }
401
402 if (isEmptyThread(m_persisterThread)) {
403 m_persisterThread = std::thread(&LogPersister::ReceiveLogLoop, shared_from_this());
404 } else {
405 std::cout << __PRETTY_FUNCTION__ << " Persister thread already started!\n";
406 }
407 }
408
ReceiveLogLoop()409 int LogPersister::ReceiveLogLoop()
410 {
411 prctl(PR_SET_NAME, "hilogd.pst");
412 std::cout << __PRETTY_FUNCTION__ << " " << std::this_thread::get_id() << "\n";
413 for (;;) {
414 if (m_stopThread) {
415 break;
416 }
417
418 auto result = m_hilogBuffer.Query(m_filters, m_bufReader, [this](const HilogData& logData) {
419 if (WriteLogData(logData)) {
420 std::cerr << __PRETTY_FUNCTION__ << " Can't write new log data!\n";
421 }
422 });
423
424 if (!result) {
425 std::unique_lock<decltype(m_receiveLogCvMtx)> lk(m_receiveLogCvMtx);
426 m_receiveLogCv.wait_for(lk, m_baseData.newLogTimeout);
427 }
428 }
429 WriteCompressedLogs();
430 m_fileRotator->FinishInput();
431 return 0;
432 }
433
Query(uint16_t logType,std::list<LogPersistQueryResult> & results)434 int LogPersister::Query(uint16_t logType, std::list<LogPersistQueryResult> &results)
435 {
436 std::lock_guard<decltype(s_logPersistersMtx)> guard(s_logPersistersMtx);
437 std::cout << __PRETTY_FUNCTION__ << " Persister.Query: logType " << logType << "\n";
438 for (auto& logPersister : s_logPersisters) {
439 uint16_t currentType = logPersister->m_filters.inclusions.types;
440 std::cout << __PRETTY_FUNCTION__ << " Persister.Query: (*it)->queryCondition.types " << currentType << "\n";
441 if (currentType & logType) {
442 LogPersistQueryResult response;
443 response.logType = currentType;
444 logPersister->FillInfo(response);
445 results.push_back(response);
446 }
447 }
448 return 0;
449 }
450
FillInfo(LogPersistQueryResult & response)451 void LogPersister::FillInfo(LogPersistQueryResult &response)
452 {
453 response.jobId = m_baseData.id;
454 if (strcpy_s(response.filePath, FILE_PATH_MAX_LEN, m_baseData.logPath.c_str())) {
455 return;
456 }
457 response.compressAlg = m_baseData.compressAlg;
458 response.fileSize = m_baseData.logFileSizeLimit;
459 response.fileNum = m_baseData.maxLogFileNum;
460 }
461
Kill(uint32_t id)462 int LogPersister::Kill(uint32_t id)
463 {
464 auto logPersisterPtr = GetLogPersisterById(id);
465 if (logPersisterPtr) {
466 return logPersisterPtr->Deinit();
467 }
468 std::cerr << __PRETTY_FUNCTION__ << " Log persister with id: " << id << " does not exist.\n";
469 return ERR_LOG_PERSIST_JOBID_FAIL;
470 }
471
Stop()472 void LogPersister::Stop()
473 {
474 std::cout << __PRETTY_FUNCTION__ << " Exiting LogPersister!\n";
475 if (isEmptyThread(m_persisterThread)) {
476 std::cout << __PRETTY_FUNCTION__ << " Thread was exited or not started!\n";
477 return;
478 }
479
480 m_stopThread = true;
481 m_receiveLogCv.notify_all();
482
483 if (m_persisterThread.joinable()) {
484 m_persisterThread.join();
485 }
486 }
487
CheckRegistered(uint32_t id,const std::string & logPath)488 bool LogPersister::CheckRegistered(uint32_t id, const std::string& logPath)
489 {
490 std::lock_guard<decltype(s_logPersistersMtx)> lock(s_logPersistersMtx);
491 auto it = std::find_if(s_logPersisters.begin(), s_logPersisters.end(),
492 [&](const std::shared_ptr<LogPersister>& logPersister) {
493 if (logPersister->m_baseData.logPath == logPath || logPersister->m_baseData.id == id) {
494 return true;
495 }
496 return false;
497 });
498 return it != s_logPersisters.end();
499 }
500
GetLogPersisterById(uint32_t id)501 std::shared_ptr<LogPersister> LogPersister::GetLogPersisterById(uint32_t id)
502 {
503 std::lock_guard<decltype(s_logPersistersMtx)> guard(s_logPersistersMtx);
504
505 auto it = std::find_if(s_logPersisters.begin(), s_logPersisters.end(),
506 [&](const std::shared_ptr<LogPersister>& logPersister) {
507 if (logPersister->m_baseData.id == id) {
508 return true;
509 }
510 return false;
511 });
512 if (it == s_logPersisters.end()) {
513 return std::shared_ptr<LogPersister>();
514 }
515 return *it;
516 }
517
RegisterLogPersister(const std::shared_ptr<LogPersister> & obj)518 void LogPersister::RegisterLogPersister(const std::shared_ptr<LogPersister>& obj)
519 {
520 std::lock_guard<decltype(s_logPersistersMtx)> lock(s_logPersistersMtx);
521 s_logPersisters.push_back(obj);
522 }
523
DeregisterLogPersister(const std::shared_ptr<LogPersister> & obj)524 void LogPersister::DeregisterLogPersister(const std::shared_ptr<LogPersister>& obj)
525 {
526 if (!obj) {
527 std::cerr << __PRETTY_FUNCTION__ << " Invalid invoke - this should never happened!\n";
528 return;
529 }
530 std::lock_guard<decltype(s_logPersistersMtx)> lock(s_logPersistersMtx);
531 auto it = std::find_if(s_logPersisters.begin(), s_logPersisters.end(),
532 [&](const std::shared_ptr<LogPersister>& logPersister) {
533 if (logPersister->m_baseData.id == obj->m_baseData.id) {
534 return true;
535 }
536 return false;
537 });
538 if (it == s_logPersisters.end()) {
539 std::cerr << __PRETTY_FUNCTION__ << " Inconsistent data - this should never happended!\n";
540 return;
541 }
542 s_logPersisters.erase(it);
543 }
544 } // namespace HiviewDFX
545 } // namespace OHOS
546