• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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