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 "trace_utils.h"
17 
18 #include <cinttypes>
19 #include <ctime>
20 #include <dirent.h>
21 #include <fcntl.h>
22 #include <filesystem>
23 #include <sys/file.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 
27 #include "hilog/log.h"
28 
29 namespace OHOS {
30 namespace HiviewDFX {
31 namespace Hitrace {
32 namespace {
33 const std::string TRACE_DEFAULT_DIR = "/data/log/hitrace/";
34 const std::string TRACE_EVENT_FORMT = "saved_events_format";
35 const std::string TRACE_SNAPSHOT_PREFIX = "trace_";
36 const std::string TRACE_RECORDING_PREFIX = "record_trace_";
37 const size_t DEFAULT_TRACE_FILE_LIMIT = 15;
38 
39 struct FileWithTime {
40     std::string filename;
41     time_t ctime;
42 };
43 
RemoveFile(const std::string& fileName)44 void RemoveFile(const std::string& fileName)
45 {
46     int fd = open(fileName.c_str(), O_RDONLY | O_NONBLOCK);
47     if (fd == -1) {
48         HILOG_WARN(LOG_CORE, "RemoveFile :: open file failed: %{public}s", fileName.c_str());
49         return;
50     }
51     if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
52         HILOG_WARN(LOG_CORE, "RemoveFile :: get file lock failed, skip remove: %{public}s", fileName.c_str());
53         close(fd);
54         return;
55     }
56     if (remove(fileName.c_str()) == 0) {
57         HILOG_INFO(LOG_CORE, "RemoveFile :: Delete %{public}s success.", fileName.c_str());
58     } else {
59         HILOG_WARN(LOG_CORE, "RemoveFile :: Delete %{public}s failed.", fileName.c_str());
60     }
61     flock(fd, LOCK_UN);
62     close(fd);
63 }
64 
GetRecordTraceFilesInDir(std::vector<FileWithTime>& fileList)65 void GetRecordTraceFilesInDir(std::vector<FileWithTime>& fileList)
66 {
67     struct stat fileStat;
68     for (const auto &entry : std::filesystem::directory_iterator(TRACE_DEFAULT_DIR)) {
69         if (!entry.is_regular_file()) {
70             continue;
71         }
72         std::string fileName = entry.path().filename().string();
73         if (fileName.substr(0, TRACE_RECORDING_PREFIX.size()) == TRACE_RECORDING_PREFIX) {
74             if (stat((TRACE_DEFAULT_DIR + fileName).c_str(), &fileStat) == 0) {
75                 fileList.push_back({fileName, fileStat.st_ctime});
76             }
77         }
78     }
79     std::sort(fileList.begin(), fileList.end(), [](const FileWithTime& a, const FileWithTime& b) {
80         return a.ctime < b.ctime;
81     });
82 }
83 }
84 
GenerateTraceFileName(bool isSnapshot)85 std::string GenerateTraceFileName(bool isSnapshot)
86 {
87     // eg: /data/log/hitrace/trace_localtime@boottime.sys
88     std::string name = TRACE_DEFAULT_DIR;
89 
90     if (isSnapshot) {
91         name += TRACE_SNAPSHOT_PREFIX;
92     } else {
93         name += TRACE_RECORDING_PREFIX;
94     }
95 
96     // get localtime
97     time_t currentTime = time(nullptr);
98     struct tm timeInfo = {};
99     const int bufferSize = 16;
100     char timeStr[bufferSize] = {0};
101     if (localtime_r(&currentTime, &timeInfo) == nullptr) {
102         HILOG_ERROR(LOG_CORE, "Failed to get localtime.");
103         return "";
104     }
105     (void)strftime(timeStr, bufferSize, "%Y%m%d%H%M%S", &timeInfo);
106     name += std::string(timeStr);
107     // get boottime
108     struct timespec bts = {0, 0};
109     clock_gettime(CLOCK_BOOTTIME, &bts);
110     name += "@" + std::to_string(bts.tv_sec) + "-" + std::to_string(bts.tv_nsec) + ".sys";
111 
112     struct timespec mts = {0, 0};
113     clock_gettime(CLOCK_MONOTONIC, &mts);
114     HILOG_INFO(LOG_CORE, "output trace: %{public}s, boot_time(%{public}" PRId64 "), mono_time(%{public}" PRId64 ").",
115         name.c_str(), static_cast<int64_t>(bts.tv_sec), static_cast<int64_t>(mts.tv_sec));
116     return name;
117 }
118 
119 /**
120  * When the SERVICE_MODE is started, clear the remaining trace files in the folder.
121 */
DelSnapshotTraceFile(const bool deleteSavedFmt, const int keepFileCount)122 void DelSnapshotTraceFile(const bool deleteSavedFmt, const int keepFileCount)
123 {
124     if (access(TRACE_DEFAULT_DIR.c_str(), F_OK) != 0) {
125         return;
126     }
127     DIR* dirPtr = opendir(TRACE_DEFAULT_DIR.c_str());
128     if (dirPtr == nullptr) {
129         HILOG_ERROR(LOG_CORE, "Failed to opendir %{public}s.", TRACE_DEFAULT_DIR.c_str());
130         return;
131     }
132     std::vector<FileWithTime> snapshotTraceFiles;
133     struct dirent* ptr = nullptr;
134     struct stat fileStat;
135     while ((ptr = readdir(dirPtr)) != nullptr) {
136         if (ptr->d_type == DT_REG) {
137             std::string name = std::string(ptr->d_name);
138             if (deleteSavedFmt && name.compare(0, TRACE_EVENT_FORMT.size(), TRACE_EVENT_FORMT) == 0) {
139                 RemoveFile(TRACE_DEFAULT_DIR + name);
140                 continue;
141             }
142             if (name.compare(0, TRACE_SNAPSHOT_PREFIX.size(), TRACE_SNAPSHOT_PREFIX) != 0) {
143                 continue;
144             }
145             if (stat((TRACE_DEFAULT_DIR + name).c_str(), &fileStat) == 0) {
146                 snapshotTraceFiles.push_back({name, fileStat.st_ctime});
147             }
148         }
149     }
150     closedir(dirPtr);
151 
152     std::sort(snapshotTraceFiles.begin(), snapshotTraceFiles.end(), [](const FileWithTime& a, const FileWithTime& b) {
153         return a.ctime < b.ctime;
154     });
155 
156     int snapshotTraceFilesSize = static_cast<int>(snapshotTraceFiles.size());
157     int deleteFileCnt = snapshotTraceFilesSize - keepFileCount;
158     for (int i = 0; i < deleteFileCnt && i < snapshotTraceFilesSize; i++) {
159         RemoveFile(TRACE_DEFAULT_DIR + snapshotTraceFiles[i].filename);
160     }
161 }
162 
163 /**
164  * open trace file aging mechanism
165  */
DelOldRecordTraceFile(const int& fileLimit)166 void DelOldRecordTraceFile(const int& fileLimit)
167 {
168     size_t traceFileLimit = DEFAULT_TRACE_FILE_LIMIT;
169     if (fileLimit != 0) {
170         traceFileLimit = static_cast<size_t>(fileLimit);
171     }
172     HILOG_INFO(LOG_CORE, "DelOldRecordTraceFile: activate aging mechanism with file limit %{public}zu", traceFileLimit);
173 
174     std::vector<FileWithTime> fileList;
175     GetRecordTraceFilesInDir(fileList);
176 
177     if (fileList.size() <= traceFileLimit) {
178         HILOG_INFO(LOG_CORE, "DelOldRecordTraceFile: no record trace file need be deleted.");
179         return;
180     }
181 
182     size_t deleteNum = fileList.size() - traceFileLimit;
183     for (size_t i = 0; i < deleteNum; ++i) {
184         if (remove((TRACE_DEFAULT_DIR + fileList[i].filename).c_str()) == 0) {
185             HILOG_INFO(LOG_CORE, "DelOldRecordTraceFile: delete first: %{public}s success.",
186                 fileList[i].filename.c_str());
187         } else {
188             HILOG_ERROR(LOG_CORE, "DelOldRecordTraceFile: delete first: %{public}s failed, errno: %{public}d.",
189                 fileList[i].filename.c_str(), errno);
190         }
191     }
192 }
193 
ClearOldTraceFile(std::vector<std::string>& fileLists, const int& fileLimit)194 void ClearOldTraceFile(std::vector<std::string>& fileLists, const int& fileLimit)
195 {
196     if (fileLists.size() == 0) {
197         return;
198     }
199 
200     size_t traceFileLimit = DEFAULT_TRACE_FILE_LIMIT;
201     if (fileLimit != 0) {
202         traceFileLimit = static_cast<size_t>(fileLimit);
203     }
204     HILOG_INFO(LOG_CORE, "ClearOldTraceFile: activate aging mechanism with file limit %{public}zu", traceFileLimit);
205 
206     if (fileLists.size() > traceFileLimit && access(fileLists[0].c_str(), F_OK) == 0) {
207         if (remove(fileLists[0].c_str()) == 0) {
208             fileLists.erase(fileLists.begin());
209             HILOG_INFO(LOG_CORE, "ClearOldTraceFile: delete first success.");
210         } else {
211             HILOG_ERROR(LOG_CORE, "ClearOldTraceFile: delete first failed, errno: %{public}d.", errno);
212         }
213     }
214 }
215 
216 /**
217  * When the raw trace is started, clear the saved_events_format files in the folder.
218  */
DelSavedEventsFormat()219 void DelSavedEventsFormat()
220 {
221     const std::string savedEventsFormatPath = TRACE_DEFAULT_DIR + TRACE_EVENT_FORMT;
222     if (access(savedEventsFormatPath.c_str(), F_OK) != 0) {
223         // saved_events_format not exit
224         return;
225     }
226     // saved_events_format exit
227     if (remove(savedEventsFormatPath.c_str()) == 0) {
228         HILOG_INFO(LOG_CORE, "Delete saved_events_format success.");
229     } else {
230         HILOG_ERROR(LOG_CORE, "Delete saved_events_format failed.");
231     }
232 }
233 } // namespace Hitrace
234 } // namespace HiviewDFX
235 } // namespace OHOS
236