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 <dirent.h>
33 #include <iostream>
34 #include <mutex>
35 #include <regex>
36 #include <sstream>
37 #include <string>
38 #include <thread>
39 #include <unistd.h>
40
41 #include <hilog_common.h>
42 #include <log_buffer.h>
43 #include <log_compress.h>
44 #include <log_print.h>
45 #include <log_utils.h>
46
47 namespace OHOS {
48 namespace HiviewDFX {
49 using namespace std;
50
51 static const int MAX_LOG_WRITE_INTERVAL = 5;
52
IsEmptyThread(const std::thread & th)53 static bool IsEmptyThread(const std::thread& th)
54 {
55 static const std::thread EMPTY_THREAD;
56 return th.get_id() == EMPTY_THREAD.get_id();
57 }
58
59 std::recursive_mutex LogPersister::s_logPersistersMtx;
60 std::list<std::shared_ptr<LogPersister>> LogPersister::s_logPersisters;
61
CreateLogPersister(HilogBuffer & buffer)62 std::shared_ptr<LogPersister> LogPersister::CreateLogPersister(HilogBuffer &buffer)
63 {
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 m_startMsg = { 0 };
72 }
73
~LogPersister()74 LogPersister::~LogPersister()
75 {
76 m_hilogBuffer.RemoveBufReader(m_bufReader);
77 Deinit();
78 }
79
InitCompression()80 int LogPersister::InitCompression()
81 {
82 m_compressBuffer = std::make_unique<LogPersisterBuffer>();
83 if (!m_compressBuffer) {
84 return RET_FAIL;
85 }
86 switch (m_startMsg.compressAlg) {
87 case COMPRESS_TYPE_NONE:
88 m_compressor = std::make_unique<NoneCompress>();
89 break;
90 case COMPRESS_TYPE_ZLIB:
91 m_compressor = std::make_unique<ZlibCompress>();
92 break;
93 case COMPRESS_TYPE_ZSTD:
94 m_compressor = std::make_unique<ZstdCompress>();
95 break;
96 default:
97 break;
98 }
99 if (!m_compressor) {
100 return RET_FAIL;
101 }
102 return RET_SUCCESS;
103 }
104
InitFileRotator(const PersistRecoveryInfo & info,bool restore)105 int LogPersister::InitFileRotator(const PersistRecoveryInfo& info, bool restore)
106 {
107 std::string fileSuffix = "";
108 switch (m_startMsg.compressAlg) {
109 case CompressAlg::COMPRESS_TYPE_ZSTD:
110 fileSuffix = ".zst";
111 break;
112 case CompressAlg::COMPRESS_TYPE_ZLIB:
113 fileSuffix = ".gz";
114 break;
115 default:
116 break;
117 }
118 m_fileRotator = std::make_unique<LogPersisterRotator>(m_startMsg.filePath,
119 m_startMsg.jobId, m_startMsg.fileNum, fileSuffix);
120 if (!m_fileRotator) {
121 std::cerr << "Not enough memory!\n";
122 return RET_FAIL;
123 }
124 return m_fileRotator->Init(info, restore);
125 }
126
Init(const PersistRecoveryInfo & info,bool restore)127 int LogPersister::Init(const PersistRecoveryInfo& info, bool restore)
128 {
129 std::cout << "Persist init begin\n";
130 std::lock_guard<decltype(m_initMtx)> lock(m_initMtx);
131 if (m_inited) {
132 return 0;
133 }
134 m_startMsg = info.msg;
135
136 std::string path = m_startMsg.filePath;
137 size_t separatorPos = path.find_last_of('/');
138 if (separatorPos == std::string::npos) {
139 return ERR_LOG_PERSIST_FILE_PATH_INVALID;
140 }
141
142 std::string parentPath = path.substr(0, separatorPos);
143 if (access(parentPath.c_str(), F_OK) != 0) {
144 perror("persister directory does not exist.");
145 return ERR_LOG_PERSIST_FILE_PATH_INVALID;
146 }
147
148 // below guard is needed to have sure only one Path and Id is reqistered till end of init!
149 std::lock_guard<decltype(s_logPersistersMtx)> guard(s_logPersistersMtx);
150 if (CheckRegistered(m_startMsg.jobId, path)) {
151 return ERR_LOG_PERSIST_TASK_EXISTED;
152 }
153 if (InitCompression() != RET_SUCCESS) {
154 return ERR_LOG_PERSIST_COMPRESS_INIT_FAIL;
155 }
156 int ret = InitFileRotator(info, restore);
157 if (ret != RET_SUCCESS) {
158 return ret;
159 }
160
161 if (int result = PrepareUncompressedFile(parentPath, restore)) {
162 return result;
163 }
164
165 RegisterLogPersister(shared_from_this());
166 m_inited = true;
167 std::cout << " Persist init done\n";
168 return 0;
169 }
170
Deinit()171 int LogPersister::Deinit()
172 {
173 std::cout << "LogPersist deinit begin\n";
174 std::lock_guard<decltype(m_initMtx)> lock(m_initMtx);
175 if (!m_inited) {
176 return 0;
177 }
178
179 Stop();
180
181 munmap(m_mappedPlainLogFile, sizeof(LogPersisterBuffer));
182 std::cout << "Removing unmapped plain log file: " << m_plainLogFilePath << "\n";
183 if (remove(m_plainLogFilePath.c_str())) {
184 std::cerr << "File: " << m_plainLogFilePath << " can't be removed. ";
185 PrintErrorno(errno);
186 }
187
188 DeregisterLogPersister(shared_from_this());
189 m_inited = false;
190 std::cout << "LogPersist deinit done\n";
191 return 0;
192 }
193
PrepareUncompressedFile(const std::string & parentPath,bool restore)194 int LogPersister::PrepareUncompressedFile(const std::string& parentPath, bool restore)
195 {
196 std::string fileName = std::string(".") + AUXILLARY_PERSISTER_PREFIX + std::to_string(m_startMsg.jobId);
197 m_plainLogFilePath = parentPath + "/" + fileName;
198 FILE* plainTextFile = fopen(m_plainLogFilePath.c_str(), restore ? "r+" : "w+");
199
200 if (!plainTextFile) {
201 std::cerr << " Open uncompressed log file(" << m_plainLogFilePath << ") failed: ";
202 PrintErrorno(errno);
203 return ERR_LOG_PERSIST_FILE_OPEN_FAIL;
204 }
205
206 if (!restore) {
207 ftruncate(fileno(plainTextFile), sizeof(LogPersisterBuffer));
208 fflush(plainTextFile);
209 fsync(fileno(plainTextFile));
210 }
211 m_mappedPlainLogFile = reinterpret_cast<LogPersisterBuffer*>(mmap(nullptr, sizeof(LogPersisterBuffer),
212 PROT_READ | PROT_WRITE, MAP_SHARED, fileno(plainTextFile), 0));
213 if (fclose(plainTextFile)) {
214 std::cerr << "File: " << plainTextFile << " can't be closed. ";
215 PrintErrorno(errno);
216 }
217 if (m_mappedPlainLogFile == MAP_FAILED) {
218 std::cerr << " mmap file failed: ";
219 PrintErrorno(errno);
220 return RET_FAIL;
221 }
222 if (restore) {
223 #ifdef DEBUG
224 std::cout << " Recovered persister, Offset=" << m_mappedPlainLogFile->offset << "\n";
225 #endif
226 // try to store previous uncompressed logs
227 auto compressionResult = m_compressor->Compress(*m_mappedPlainLogFile, *m_compressBuffer);
228 if (compressionResult != 0) {
229 std::cerr << " Compression error. Result:" << compressionResult << "\n";
230 return RET_FAIL;
231 }
232 WriteCompressedLogs();
233 } else {
234 m_mappedPlainLogFile->offset = 0;
235 }
236 return 0;
237 }
238
NotifyNewLogAvailable()239 void LogPersister::NotifyNewLogAvailable()
240 {
241 m_receiveLogCv.notify_one();
242 }
243
WriteUncompressedLogs(std::string & logLine)244 bool LogPersister::WriteUncompressedLogs(std::string& logLine)
245 {
246 uint16_t size = logLine.length();
247 uint32_t remainingSpace = MAX_PERSISTER_BUFFER_SIZE - m_mappedPlainLogFile->offset;
248 if (remainingSpace < size) {
249 return false;
250 }
251 char* currentContentPos = m_mappedPlainLogFile->content + m_mappedPlainLogFile->offset;
252 int r = memcpy_s(currentContentPos, remainingSpace, logLine.c_str(), logLine.length());
253 if (r != 0) {
254 std::cout << " Can't copy part of memory!\n";
255 return true;
256 }
257 m_mappedPlainLogFile->offset += logLine.length();
258 return true;
259 }
260
WriteLogData(const HilogData & logData)261 int LogPersister::WriteLogData(const HilogData& logData)
262 {
263 LogContent content = {
264 .level = logData.level,
265 .type = logData.type,
266 .pid = logData.pid,
267 .tid = logData.tid,
268 .domain = logData.domain,
269 .tv_sec = logData.tv_sec,
270 .tv_nsec = logData.tv_nsec,
271 .mono_sec = logData.mono_sec,
272 .tag = logData.tag,
273 .log = logData.content
274 };
275 LogFormat format = {
276 .colorful = false,
277 .timeFormat = FormatTime::TIME,
278 .timeAccuFormat = FormatTimeAccu::MSEC,
279 .year = false,
280 .zone = false,
281 };
282 std::ostringstream oss;
283 LogPrintWithFormat(content, format, oss);
284 std::string formatedLogStr = oss.str();
285 // Firstly gather uncompressed logs in auxiliary file
286 if (WriteUncompressedLogs(formatedLogStr))
287 return 0;
288 // Try to compress auxiliary file
289 auto compressionResult = m_compressor->Compress(*m_mappedPlainLogFile, *m_compressBuffer);
290 if (compressionResult != 0) {
291 std::cerr << " Compression error. Result:" << compressionResult << "\n";
292 return RET_FAIL;
293 }
294 // Write compressed buffor and clear counters
295 WriteCompressedLogs();
296 // Try again write data that wasn't written at the beginning
297 // If again fail then these logs are skipped
298 return WriteUncompressedLogs(formatedLogStr) ? 0 : RET_FAIL;
299 }
300
WriteCompressedLogs()301 inline void LogPersister::WriteCompressedLogs()
302 {
303 if (m_mappedPlainLogFile->offset == 0)
304 return;
305 m_fileRotator->Input(m_compressBuffer->content, m_compressBuffer->offset);
306 m_plainLogSize += m_mappedPlainLogFile->offset;
307 if (m_plainLogSize >= m_startMsg.fileSize) {
308 m_plainLogSize = 0;
309 m_fileRotator->FinishInput();
310 }
311 m_compressBuffer->offset = 0;
312 m_mappedPlainLogFile->offset = 0;
313 }
314
Start()315 void LogPersister::Start()
316 {
317 {
318 std::lock_guard<decltype(m_initMtx)> lock(m_initMtx);
319 if (!m_inited) {
320 std::cerr << " Log persister wasn't inited!\n";
321 return;
322 }
323 }
324
325 if (IsEmptyThread(m_persisterThread)) {
326 m_persisterThread = std::thread([shared = shared_from_this()]() {
327 shared->ReceiveLogLoop();
328 });
329 } else {
330 std::cout << " Persister thread already started!\n";
331 }
332 }
333
ReceiveLogLoop()334 int LogPersister::ReceiveLogLoop()
335 {
336 prctl(PR_SET_NAME, "hilogd.pst");
337 std::cout << "Persist ReceiveLogLoop " << std::this_thread::get_id() << "\n";
338 for (;;) {
339 if (m_stopThread) {
340 break;
341 }
342 std::optional<HilogData> data = m_hilogBuffer.Query(m_startMsg.filter, m_bufReader);
343 if (data.has_value()) {
344 if (WriteLogData(data.value())) {
345 std::cerr << " Can't write new log data!\n";
346 }
347 } else {
348 std::unique_lock<decltype(m_receiveLogCvMtx)> lk(m_receiveLogCvMtx);
349 static const std::chrono::seconds waitTime(MAX_LOG_WRITE_INTERVAL);
350 if (cv_status::timeout == m_receiveLogCv.wait_for(lk, waitTime)) {
351 std::cout << "no log timeout, write log forcely" << std::endl;
352 (void)m_compressor->Compress(*m_mappedPlainLogFile, *m_compressBuffer);
353 WriteCompressedLogs();
354 }
355 }
356 }
357 // try to compress the remaining log in cache
358 (void)m_compressor->Compress(*m_mappedPlainLogFile, *m_compressBuffer);
359 WriteCompressedLogs();
360 m_fileRotator->FinishInput();
361 return 0;
362 }
363
Query(std::list<LogPersistQueryResult> & results)364 int LogPersister::Query(std::list<LogPersistQueryResult> &results)
365 {
366 std::lock_guard<decltype(s_logPersistersMtx)> guard(s_logPersistersMtx);
367 for (auto& logPersister : s_logPersisters) {
368 LogPersistQueryResult response;
369 response.logType = logPersister->m_startMsg.filter.types;
370 logPersister->FillInfo(response);
371 results.push_back(response);
372 }
373 return 0;
374 }
375
FillInfo(LogPersistQueryResult & response)376 void LogPersister::FillInfo(LogPersistQueryResult &response)
377 {
378 response.jobId = m_startMsg.jobId;
379 if (strcpy_s(response.filePath, FILE_PATH_MAX_LEN, m_startMsg.filePath)) {
380 return;
381 }
382 response.compressAlg = m_startMsg.compressAlg;
383 response.fileSize = m_startMsg.fileSize;
384 response.fileNum = m_startMsg.fileNum;
385 }
386
Kill(uint32_t id)387 int LogPersister::Kill(uint32_t id)
388 {
389 auto logPersisterPtr = GetLogPersisterById(id);
390 if (logPersisterPtr) {
391 return logPersisterPtr->Deinit();
392 }
393 std::cerr << " Log persister with id: " << id << " does not exist.\n";
394 return ERR_LOG_PERSIST_JOBID_FAIL;
395 }
396
Stop()397 void LogPersister::Stop()
398 {
399 std::cout << "Exiting LogPersister!\n";
400 if (IsEmptyThread(m_persisterThread)) {
401 std::cout << "Thread was exited or not started!\n";
402 return;
403 }
404
405 m_stopThread = true;
406 m_receiveLogCv.notify_all();
407
408 if (m_persisterThread.joinable()) {
409 m_persisterThread.join();
410 }
411 }
412
Refresh(uint32_t id)413 int LogPersister::Refresh(uint32_t id)
414 {
415 auto logPersisterPtr = GetLogPersisterById(id);
416 if (logPersisterPtr) {
417 std::optional<HilogData> data = logPersisterPtr->m_hilogBuffer.Query(logPersisterPtr->m_startMsg.filter,
418 logPersisterPtr->m_bufReader);
419 if (data.has_value()) {
420 if (logPersisterPtr->WriteLogData(data.value())) {
421 std::cerr << " Can't write new log data!\n";
422 }
423 } else {
424 std::unique_lock<decltype(logPersisterPtr->m_receiveLogCvMtx)> lk(logPersisterPtr->m_receiveLogCvMtx);
425 static const std::chrono::seconds waitTime(MAX_LOG_WRITE_INTERVAL);
426 if (cv_status::timeout == logPersisterPtr->m_receiveLogCv.wait_for(lk, waitTime)) {
427 std::cout << "no log timeout, write log forcely" << std::endl;
428 (void)logPersisterPtr->m_compressor->Compress(*(logPersisterPtr->m_mappedPlainLogFile),
429 *(logPersisterPtr->m_compressBuffer));
430 logPersisterPtr->WriteCompressedLogs();
431 }
432 }
433 return 0;
434 }
435 std::cerr << " Log persister with id: " << id << " does not exist.\n";
436 return ERR_LOG_PERSIST_JOBID_FAIL;
437 }
438
Clear()439 void LogPersister::Clear()
440 {
441 std::regex hilogFilePattern("^hilog.*gz$");
442 DIR *dir = nullptr;
443 struct dirent *ent = nullptr;
444 if ((dir = opendir(HILOG_FILE_DIR)) != nullptr) {
445 while ((ent = readdir(dir)) != nullptr) {
446 size_t length = strlen(ent->d_name);
447 std::string dName(ent->d_name, length);
448 if (std::regex_match(dName, hilogFilePattern)) {
449 remove((HILOG_FILE_DIR + dName).c_str());
450 }
451 }
452 }
453 if (dir != nullptr) {
454 closedir(dir);
455 }
456 }
457
CheckRegistered(uint32_t id,const std::string & logPath)458 bool LogPersister::CheckRegistered(uint32_t id, const std::string& logPath)
459 {
460 std::lock_guard<decltype(s_logPersistersMtx)> lock(s_logPersistersMtx);
461 auto it = std::find_if(s_logPersisters.begin(), s_logPersisters.end(),
462 [&](const std::shared_ptr<LogPersister>& logPersister) {
463 if (logPersister->m_startMsg.jobId == id || logPersister->m_startMsg.filePath == logPath) {
464 return true;
465 }
466 return false;
467 });
468 return it != s_logPersisters.end();
469 }
470
GetLogPersisterById(uint32_t id)471 std::shared_ptr<LogPersister> LogPersister::GetLogPersisterById(uint32_t id)
472 {
473 std::lock_guard<decltype(s_logPersistersMtx)> guard(s_logPersistersMtx);
474
475 auto it = std::find_if(s_logPersisters.begin(), s_logPersisters.end(),
476 [&](const std::shared_ptr<LogPersister>& logPersister) {
477 if (logPersister->m_startMsg.jobId == id) {
478 return true;
479 }
480 return false;
481 });
482 if (it == s_logPersisters.end()) {
483 return std::shared_ptr<LogPersister>();
484 }
485 return *it;
486 }
487
RegisterLogPersister(const std::shared_ptr<LogPersister> & obj)488 void LogPersister::RegisterLogPersister(const std::shared_ptr<LogPersister>& obj)
489 {
490 std::lock_guard<decltype(s_logPersistersMtx)> lock(s_logPersistersMtx);
491 s_logPersisters.push_back(obj);
492 }
493
DeregisterLogPersister(const std::shared_ptr<LogPersister> & obj)494 void LogPersister::DeregisterLogPersister(const std::shared_ptr<LogPersister>& obj)
495 {
496 if (!obj) {
497 std::cerr << " Invalid invoke - this should never happened!\n";
498 return;
499 }
500 std::lock_guard<decltype(s_logPersistersMtx)> lock(s_logPersistersMtx);
501 auto it = std::find_if(s_logPersisters.begin(), s_logPersisters.end(),
502 [&](const std::shared_ptr<LogPersister>& logPersister) {
503 if (logPersister->m_startMsg.jobId == obj->m_startMsg.jobId) {
504 return true;
505 }
506 return false;
507 });
508 if (it == s_logPersisters.end()) {
509 std::cerr << " Inconsistent data - this should never happened!\n";
510 return;
511 }
512 s_logPersisters.erase(it);
513 }
514 } // namespace HiviewDFX
515 } // namespace OHOS
516