• 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 "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