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(¤tTime, &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