1 /*
2 * Copyright (c) 2024 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 "temp_file_manager.h"
17
18 #include <bitset>
19 #include <chrono>
20 #include <cmath>
21 #include <fcntl.h>
22 #include <fstream>
23 #include <map>
24 #include <memory>
25 #include <regex>
26 #include <sys/inotify.h>
27 #include <sys/stat.h>
28 #include <sys/timerfd.h>
29 #include <unistd.h>
30
31 #include "dfx_define.h"
32 #include "dfx_log.h"
33 #include "dfx_socket_request.h"
34 #include "dfx_trace.h"
35 #include "directory_ex.h"
36 #include "file_ex.h"
37
38 #ifndef HISYSEVENT_DISABLE
39 #include "hisysevent.h"
40 #endif
41
42 namespace OHOS {
43 namespace HiviewDFX {
44
45 namespace {
46 constexpr const char *const TEMP_FILE_MANAGER_TAG = "TEMP_FILE_MANAGER";
47 constexpr uint64_t SECONDS_TO_MILLISECONDS = 1000;
48
GetTargetFileConfig(const std::function<bool (const SingleFileConfig &)> & filter)49 const SingleFileConfig* GetTargetFileConfig(const std::function<bool(const SingleFileConfig&)>& filter)
50 {
51 const auto& fileConfigs = FaultLoggerConfig::GetInstance().GetTempFileConfig().singleFileConfigs;
52 auto iter = std::find_if(fileConfigs.begin(), fileConfigs.end(), filter);
53 return iter == fileConfigs.end() ? nullptr : iter.base();
54 }
55
GetCurrentTime()56 uint64_t GetCurrentTime()
57 {
58 using namespace std::chrono;
59 return static_cast<uint64_t>(duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count());
60 }
61
GetFileSize(const std::string & filePath)62 uint64_t GetFileSize(const std::string& filePath)
63 {
64 struct stat statBuf{};
65 if (stat(filePath.c_str(), &statBuf) == 0) {
66 return statBuf.st_size;
67 }
68 return 0;
69 }
70
GetTimeFromFileName(const std::string & fileName)71 uint64_t GetTimeFromFileName(const std::string& fileName)
72 {
73 constexpr int timeStrLen = 13;
74 constexpr char timeStrSplit = '-';
75 constexpr int decimal = 10;
76 auto timeStr = fileName.substr(fileName.find_last_of(timeStrSplit) + 1, timeStrLen);
77 uint64_t num = strtoull(timeStr.c_str(), nullptr, decimal);
78 if (errno == ERANGE) {
79 DFXLOGE("%{public}s :: invalid timeStr for file: %{public}s", TEMP_FILE_MANAGER_TAG, timeStr.c_str());
80 return 0;
81 }
82 return num;
83 }
84
CreateFileDir(const std::string & filePath)85 bool CreateFileDir(const std::string& filePath)
86 {
87 if (access(filePath.c_str(), F_OK) == 0 || OHOS::ForceCreateDirectory(filePath)) {
88 return true;
89 }
90 DFXLOGE("%{public}s :: failed to create dirs: %{public}s.", TEMP_FILE_MANAGER_TAG, filePath.c_str());
91 return false;
92 }
93
RemoveTempFile(const std::string & filePath)94 bool RemoveTempFile(const std::string& filePath)
95 {
96 if (access(filePath.c_str(), F_OK) != 0) {
97 return true;
98 }
99 if (!OHOS::RemoveFile(filePath)) {
100 DFXLOGE("%{public}s :: failed to remove file: %{public}s.", TEMP_FILE_MANAGER_TAG, filePath.c_str());
101 return false;
102 }
103 DFXLOGI("%{public}s :: success to remove file: %{public}s.", TEMP_FILE_MANAGER_TAG, filePath.c_str());
104 return true;
105 }
106
RemoveTimeOutFileIfNeed(const SingleFileConfig & fileConfig,std::list<std::string> & tempFiles)107 void RemoveTimeOutFileIfNeed(const SingleFileConfig& fileConfig, std::list<std::string>& tempFiles)
108 {
109 if (fileConfig.fileExistTime < 0 || fileConfig.keepFileCount < 0 ||
110 tempFiles.size() <= static_cast<size_t>(fileConfig.keepFileCount)) {
111 return;
112 }
113 auto currentTime = GetCurrentTime();
114 tempFiles.erase(std::remove_if(tempFiles.begin(), tempFiles.end(),
115 [currentTime, &fileConfig](const std::string& tempFile) {
116 auto fileCreateTime = GetTimeFromFileName(tempFile);
117 if (fileCreateTime > currentTime ||
118 (fileCreateTime + fileConfig.fileExistTime * SECONDS_TO_MILLISECONDS < currentTime)) {
119 return RemoveTempFile(tempFile);
120 }
121 return false;
122 }), tempFiles.end());
123 }
124
ForceRemoveFileIfNeed(const SingleFileConfig & fileConfig,std::list<std::string> & tempFiles)125 void ForceRemoveFileIfNeed(const SingleFileConfig& fileConfig, std::list<std::string>& tempFiles)
126 {
127 if (fileConfig.maxFileCount < 0 || tempFiles.size() <= static_cast<size_t>(fileConfig.maxFileCount)) {
128 return;
129 }
130 if (fileConfig.keepFileCount != 0) {
131 tempFiles.sort([](std::string& lhs, const std::string& rhs) -> int {
132 return GetTimeFromFileName(lhs) < GetTimeFromFileName(rhs);
133 });
134 }
135 auto deleteNum = fileConfig.keepFileCount < 0 ? 1 : tempFiles.size() - fileConfig.keepFileCount;
136 tempFiles.erase(std::remove_if(tempFiles.begin(), tempFiles.end(),
137 [&deleteNum](const std::string& tempFile) {
138 if (deleteNum > 0 && RemoveTempFile(tempFile)) {
139 deleteNum--;
140 return true;
141 }
142 return false;
143 }), tempFiles.end());
144 }
145
GetTempFiles(const SingleFileConfig & tempFileConfig,std::list<std::string> & tempFiles)146 uint64_t GetTempFiles(const SingleFileConfig& tempFileConfig, std::list<std::string>& tempFiles)
147 {
148 std::vector<std::string> files;
149 const auto& tempFilePath = FaultLoggerConfig::GetInstance().GetTempFileConfig().tempFilePath;
150 OHOS::GetDirFiles(tempFilePath, files);
151 uint64_t fileSize = 0;
152 for (const auto& file : files) {
153 if (file.find(tempFileConfig.fileNamePrefix, tempFilePath.size()) != std::string::npos) {
154 fileSize += GetFileSize(file);
155 tempFiles.emplace_back(file);
156 }
157 }
158 return fileSize;
159 }
160
GetTempFiles(std::map<const SingleFileConfig *,std::list<std::string>> & tempFilesMap)161 uint64_t GetTempFiles(std::map<const SingleFileConfig*, std::list<std::string>>& tempFilesMap)
162 {
163 std::vector<std::string> files;
164 const auto& tempFilePath = FaultLoggerConfig::GetInstance().GetTempFileConfig().tempFilePath;
165 OHOS::GetDirFiles(tempFilePath, files);
166 uint64_t fileSize = 0;
167 for (const auto& file : files) {
168 auto fileConfig = GetTargetFileConfig([&file, &tempFilePath](const SingleFileConfig& fileConfig) {
169 return file.find(fileConfig.fileNamePrefix, tempFilePath.size()) != std::string::npos;
170 });
171 if (fileConfig == nullptr) {
172 RemoveTempFile(file);
173 continue;
174 }
175 fileSize += GetFileSize(file);
176 tempFilesMap[fileConfig].emplace_back(file);
177 }
178 return fileSize;
179 }
180 }
181
TempFileManager(EpollManager & epollManager)182 TempFileManager::TempFileManager(EpollManager& epollManager) : epollManager_(epollManager) {}
183
GetTargetFileCount(int32_t type)184 int32_t& TempFileManager::GetTargetFileCount(int32_t type)
185 {
186 auto iter = std::find_if(fileCounts_.begin(), fileCounts_.end(),
187 [type] (const std::pair<int32_t, int32_t>& pair) {
188 return pair.first == type;
189 });
190 if (iter == fileCounts_.end()) {
191 fileCounts_.emplace_back(type, 0);
192 return fileCounts_.back().second;
193 }
194 return iter->second;
195 }
196
InitTempFileWatcher()197 bool TempFileManager::InitTempFileWatcher()
198 {
199 auto& config = FaultLoggerConfig::GetInstance().GetTempFileConfig();
200 auto tempFileWatcher = TempFileWatcher::CreateInstance(*this);
201 if (tempFileWatcher == nullptr) {
202 return false;
203 }
204 constexpr uint32_t watchEvent = IN_CLOSE_WRITE | IN_MOVE | IN_CREATE | IN_DELETE | IN_DELETE_SELF;
205 if (!tempFileWatcher->AddWatchEvent(config.tempFilePath.c_str(), watchEvent)) {
206 return false;
207 }
208 return epollManager_.AddListener(std::move(tempFileWatcher));
209 }
210
ClearBigFilesOnStart(bool isSizeOverLimit,std::list<std::string> & files)211 void TempFileManager::ClearBigFilesOnStart(bool isSizeOverLimit, std::list<std::string>& files)
212 {
213 auto fileClearTime = FaultLoggerConfig::GetInstance().GetTempFileConfig().fileClearTimeAfterBoot;
214 if (isSizeOverLimit || fileClearTime == 0) {
215 std::for_each(files.begin(), files.end(), RemoveTempFile);
216 files.clear();
217 } else {
218 auto tempFileRemover = DelayTask::CreateInstance([files] {
219 std::for_each(files.begin(), files.end(), RemoveTempFile);
220 }, fileClearTime, epollManager_);
221 epollManager_.AddListener(std::move(tempFileRemover));
222 }
223 }
224
ScanTempFilesOnStart()225 void TempFileManager::ScanTempFilesOnStart()
226 {
227 std::map<const SingleFileConfig*, std::list<std::string>> tempFilesMap;
228 uint64_t filesSize = GetTempFiles(tempFilesMap);
229 bool isOverLimit = filesSize > FaultLoggerConfig::GetInstance().GetTempFileConfig().maxTempFilesSize;
230 for (auto& pair : tempFilesMap) {
231 int32_t fileType = pair.first->type;
232 if (fileType <= FaultLoggerType::JS_HEAP_LEAK_LIST && fileType >= FaultLoggerType::JS_HEAP_SNAPSHOT) {
233 ClearBigFilesOnStart(isOverLimit, pair.second);
234 }
235 ForceRemoveFileIfNeed(*pair.first, pair.second);
236 GetTargetFileCount(fileType) = static_cast<int32_t>(pair.second.size());
237 }
238 }
239
Init()240 bool TempFileManager::Init()
241 {
242 auto& tempFilePath = FaultLoggerConfig::GetInstance().GetTempFileConfig().tempFilePath;
243 if (tempFilePath.empty() || !CreateFileDir(tempFilePath)) {
244 DFXLOGE("%{public}s :: invalid temp file path %{public}s", TEMP_FILE_MANAGER_TAG, tempFilePath.c_str());
245 return false;
246 }
247 ScanTempFilesOnStart();
248 return InitTempFileWatcher();
249 }
250
CreateFileDescriptor(int32_t type,int32_t pid,int32_t tid,uint64_t time)251 int32_t TempFileManager::CreateFileDescriptor(int32_t type, int32_t pid, int32_t tid, uint64_t time)
252 {
253 const auto fileConfig = GetTargetFileConfig([type](const SingleFileConfig& fileConfig) {
254 return fileConfig.type == type;
255 });
256 if (fileConfig == nullptr) {
257 DFXLOGW("%{public}s :: failed to create fileDescriptor because of unknown file type for %{public}d",
258 TEMP_FILE_MANAGER_TAG, type);
259 return -1;
260 }
261 std::string ss = FaultLoggerConfig::GetInstance().GetTempFileConfig().tempFilePath + "/" +
262 fileConfig->fileNamePrefix + "-" + std::to_string(pid);
263 if (type == FaultLoggerType::JS_HEAP_SNAPSHOT || type == FaultLoggerType::JS_RAW_SNAPSHOT) {
264 ss += "-" + std::to_string(tid);
265 }
266 ss += "-" + std::to_string(time == 0 ? std::chrono::duration_cast<std::chrono::milliseconds>\
267 (std::chrono::system_clock::now().time_since_epoch()).count() : time);
268 if (type == FaultLoggerType::JS_RAW_SNAPSHOT) {
269 ss += ".rawheap";
270 }
271 DFXLOGI("%{public}s :: create file for path(%{public}s).", TEMP_FILE_MANAGER_TAG, ss.c_str());
272 int32_t fd = OHOS_TEMP_FAILURE_RETRY(open(ss.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP));
273 if (fd < 0) {
274 int openErrno = errno;
275 const auto& dirPath = FaultLoggerConfig::GetInstance().GetTempFileConfig().tempFilePath;
276 if (access(dirPath.c_str(), F_OK) != 0) {
277 DFXLOGE("%{public}s :: Failed to create log file, errno(%{public}d). %{public}s does not exist!!!",
278 TEMP_FILE_MANAGER_TAG, openErrno, dirPath.c_str());
279 } else {
280 DFXLOGE("%{public}s :: Failed to create log file, errno(%{public}d)", TEMP_FILE_MANAGER_TAG, openErrno);
281 }
282 }
283 return fd;
284 }
285
286 #ifndef is_ohos_lite
287 std::list<std::pair<int32_t, int64_t>> TempFileManager::crashFileRecords_{};
288
ClearTimeOutRecords()289 void TempFileManager::ClearTimeOutRecords()
290 {
291 #ifdef FAULTLOGGERD_TEST
292 constexpr int validTime = 1;
293 #else
294 constexpr int validTime = 8;
295 #endif
296 auto currentTime = time(nullptr);
297 crashFileRecords_.remove_if([currentTime](const std::pair<int32_t, int32_t>& pair) {
298 return pair.second + validTime <= currentTime;
299 });
300 }
301
CheckCrashFileRecord(int32_t pid)302 bool TempFileManager::CheckCrashFileRecord(int32_t pid)
303 {
304 DFX_TRACE_SCOPED("CheckCrashFileRecord");
305 ClearTimeOutRecords();
306 auto iter = std::find_if(crashFileRecords_.begin(), crashFileRecords_.end(),
307 [pid](const auto& record) {
308 return record.first == pid;
309 });
310 return iter != crashFileRecords_.end();
311 }
312
RecordFileCreation(int32_t type,int32_t pid)313 void TempFileManager::RecordFileCreation(int32_t type, int32_t pid)
314 {
315 ClearTimeOutRecords();
316 if (type != FaultLoggerType::CPP_CRASH) {
317 return;
318 }
319 auto iter = std::find_if(crashFileRecords_.begin(), crashFileRecords_.end(),
320 [pid](const auto& record) {
321 return record.first == pid;
322 });
323 if (iter != crashFileRecords_.end()) {
324 iter->second = time(nullptr);
325 } else {
326 crashFileRecords_.emplace_back(pid, time(nullptr));
327 }
328 }
329 #endif
330
CreateInstance(TempFileManager & tempFileManager)331 std::unique_ptr<TempFileManager::TempFileWatcher> TempFileManager::TempFileWatcher::CreateInstance(
332 TempFileManager& tempFileManager)
333 {
334 int32_t watchFd = inotify_init();
335 if (watchFd < 0) {
336 DFXLOGE("%{public}s :: failed to init inotify fd: %{public}d.", TEMP_FILE_MANAGER_TAG, watchFd);
337 return nullptr;
338 }
339 return std::unique_ptr<TempFileManager::TempFileWatcher>(new (std::nothrow)TempFileWatcher(tempFileManager,
340 watchFd));
341 }
342
TempFileWatcher(TempFileManager & tempFileManager,int32_t fd)343 TempFileManager::TempFileWatcher::TempFileWatcher(TempFileManager& tempFileManager, int32_t fd)
344 : EpollListener(fd), tempFileManager_(tempFileManager) {}
345
AddWatchEvent(const char * watchPath,uint32_t watchEvent)346 bool TempFileManager::TempFileWatcher::AddWatchEvent(const char* watchPath, uint32_t watchEvent)
347 {
348 if (watchPath == nullptr) {
349 return false;
350 }
351 if (inotify_add_watch(GetFd(), watchPath, watchEvent) < 0) {
352 DFXLOGE("%{public}s :: failed to add watch for file: %{public}s.", TEMP_FILE_MANAGER_TAG, watchPath);
353 return false;
354 }
355 return true;
356 }
357
OnEventPoll()358 void TempFileManager::TempFileWatcher::OnEventPoll()
359 {
360 constexpr uint32_t eventLen = static_cast<uint32_t>(sizeof(inotify_event));
361 constexpr uint32_t eventLenSize = 32;
362 constexpr uint32_t buffLen = eventLenSize * eventLen;
363 constexpr uint32_t bound = buffLen - eventLen;
364 char eventBuf[buffLen] = {0};
365 auto readLen = static_cast<size_t>(OHOS_TEMP_FAILURE_RETRY(read(GetFd(), eventBuf, sizeof(eventBuf))));
366 size_t eventPos = 0;
367 while (readLen >= eventLen && eventPos < bound) {
368 auto *event = reinterpret_cast<inotify_event *>(eventBuf + eventPos);
369 if (event->mask & IN_DELETE_SELF) {
370 HandleDirRemoved();
371 return;
372 }
373 if (event->len > 0) {
374 std::string fileName(event->name);
375 auto fileConfig = GetTargetFileConfig([&fileName](const SingleFileConfig& fileConfig) {
376 return fileName.find(fileConfig.fileNamePrefix) != std::string::npos;
377 });
378 std::string filePath = FaultLoggerConfig::GetInstance().GetTempFileConfig().tempFilePath + "/" + fileName;
379 if (fileConfig == nullptr) {
380 RemoveTempFile(filePath);
381 } else {
382 HandleEvent(event->mask, filePath, *fileConfig);
383 }
384 }
385 auto eventSize = (eventLen + event->len);
386 readLen -= eventSize;
387 eventPos += eventSize;
388 }
389 }
390
HandleEvent(uint32_t eventMask,const std::string & filePath,const SingleFileConfig & fileConfig)391 void TempFileManager::TempFileWatcher::HandleEvent(uint32_t eventMask, const std::string& filePath,
392 const SingleFileConfig& fileConfig)
393 {
394 if (eventMask & IN_CREATE) {
395 HandleFileCreate(filePath, fileConfig);
396 }
397 if (eventMask & (IN_MOVED_FROM | IN_DELETE)) {
398 HandleFileDeleteOrMove(filePath, fileConfig);
399 }
400 if (eventMask & IN_CLOSE_WRITE) {
401 HandleFileWrite(filePath, fileConfig);
402 }
403 if (eventMask & IN_MOVED_TO) {
404 HandleFileCreate(filePath, fileConfig);
405 HandleFileWrite(filePath, fileConfig);
406 }
407 }
408
HandleFileCreate(const std::string & filePath,const SingleFileConfig & fileConfig)409 void TempFileManager::TempFileWatcher::HandleFileCreate(const std::string& filePath, const SingleFileConfig& fileConfig)
410 {
411 int32_t currentFileCount = ++(tempFileManager_.GetTargetFileCount(fileConfig.type));
412 DFXLOGD("%{public}s :: file %{public}s is created, currentFileCount: %{public}d, keepFileCount: %{public}d, "
413 "maxFileCount: %{public}d, existTime %{public}d, overTimeDeleteType %{public}d",
414 TEMP_FILE_MANAGER_TAG, filePath.c_str(), currentFileCount, fileConfig.keepFileCount,
415 fileConfig.maxFileCount, fileConfig.fileExistTime, fileConfig.overTimeFileDeleteType);
416 if (fileConfig.overTimeFileDeleteType == OverTimeFileDeleteType::ACTIVE) {
417 auto tempFileRemover = DelayTask::CreateInstance([filePath] {
418 RemoveTempFile(filePath);
419 }, fileConfig.fileExistTime, tempFileManager_.epollManager_);
420 tempFileManager_.epollManager_.AddListener(std::move(tempFileRemover));
421 }
422 if ((fileConfig.keepFileCount >= 0 && currentFileCount > fileConfig.keepFileCount) ||
423 (fileConfig.maxFileCount >= 0 && currentFileCount > fileConfig.maxFileCount)) {
424 std::list<std::string> files;
425 GetTempFiles(fileConfig, files);
426 RemoveTimeOutFileIfNeed(fileConfig, files);
427 ForceRemoveFileIfNeed(fileConfig, files);
428 }
429 }
430
HandleFileDeleteOrMove(const std::string & filePath,const SingleFileConfig & fileConfig)431 void TempFileManager::TempFileWatcher::HandleFileDeleteOrMove(const std::string& filePath,
432 const SingleFileConfig& fileConfig)
433 {
434 int32_t& currentFileCount = tempFileManager_.GetTargetFileCount(fileConfig.type);
435 if (currentFileCount > 0) {
436 currentFileCount--;
437 }
438 DFXLOGD("%{public}s :: file %{public}s is deleted or moved, currentFileCount: %{public}d",
439 TEMP_FILE_MANAGER_TAG, filePath.c_str(), currentFileCount);
440 }
441
HandleFileWrite(const std::string & filePath,const SingleFileConfig & fileConfig)442 void TempFileManager::TempFileWatcher::HandleFileWrite(const std::string& filePath, const SingleFileConfig& fileConfig)
443 {
444 if (fileConfig.maxSingleFileSize == 0 || access(filePath.c_str(), F_OK) != 0) {
445 return;
446 }
447 if (GetFileSize(filePath) <= fileConfig.maxSingleFileSize) {
448 return;
449 }
450 DFXLOGW("%{public}s :: filesize of: %{public}s is over limit %{public}" PRIu64,
451 TEMP_FILE_MANAGER_TAG, filePath.c_str(), fileConfig.maxSingleFileSize);
452 if (fileConfig.overFileSizeAction == OverFileSizeAction::DELETE) {
453 RemoveTempFile(filePath);
454 } else {
455 truncate64(filePath.c_str(), static_cast<off64_t>(fileConfig.maxSingleFileSize));
456 }
457 }
458
HandleDirRemoved()459 void TempFileManager::TempFileWatcher::HandleDirRemoved()
460 {
461 std::string summary = "The temp file directory: " +
462 FaultLoggerConfig::GetInstance().GetTempFileConfig().tempFilePath + " was removed unexpectedly";
463 DFXLOGE("%{public}s :: %{public}s", TEMP_FILE_MANAGER_TAG, summary.c_str());
464 #ifndef HISYSEVENT_DISABLE
465 auto now = std::chrono::system_clock::now();
466 auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
467 HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::RELIABILITY, "CPP_CRASH_NO_LOG",
468 OHOS::HiviewDFX::HiSysEvent::EventType::FAULT,
469 "HAPPEN_TIME", timestamp,
470 "SUMMARY", summary);
471 #endif
472 tempFileManager_.epollManager_.RemoveListener(GetFd());
473 }
474 }
475 }
476