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_file_utils.h"
17
18 #include <cinttypes>
19 #include <cstdio>
20 #include <ctime>
21 #include <dirent.h>
22 #include <fcntl.h>
23 #include <filesystem>
24 #include <sys/file.h>
25 #include <sys/stat.h>
26 #include <sys/sysinfo.h>
27 #include <sys/types.h>
28 #include <securec.h>
29 #include <unistd.h>
30
31 #include "common_utils.h"
32 #include "common_define.h"
33 #include "hilog/log.h"
34
35 namespace OHOS {
36 namespace HiviewDFX {
37 namespace Hitrace {
38 #ifdef LOG_DOMAIN
39 #undef LOG_DOMAIN
40 #define LOG_DOMAIN 0xD002D33
41 #endif
42 #ifdef LOG_TAG
43 #undef LOG_TAG
44 #define LOG_TAG "HitraceUtils"
45 #endif
46 namespace {
47 #if defined(RECORD_FILE_LIMIT) && (RECORD_FILE_LIMIT != 0)
48 const size_t DEFAULT_TRACE_FILE_LIMIT = RECORD_FILE_LIMIT;
49 #else
50 const size_t DEFAULT_TRACE_FILE_LIMIT = 15;
51 #endif
52 const int TIME_BUFFER_SIZE = 16;
53 const int DEFAULT_TRACE_DURATION = 30;
54 const int TIME_INIT = 1900;
55 constexpr uint64_t MS_TO_NS = 1000000;
56 constexpr uint64_t S_TO_MS = 1000;
57 constexpr uint64_t S_TO_NS = 1000000000;
58 const std::string TRACE_SNAPSHOT_PREFIX = "trace_";
59 const std::string TRACE_RECORDING_PREFIX = "record_trace_";
60 const std::string TRACE_CACHE_PREFIX = "cache_trace_";
61 std::map<TRACE_TYPE, std::string> tracePrefixMap = {
62 {TRACE_SNAPSHOT, TRACE_SNAPSHOT_PREFIX},
63 {TRACE_RECORDING, TRACE_RECORDING_PREFIX},
64 {TRACE_CACHE, TRACE_CACHE_PREFIX},
65 };
66
GetTraceFilesInDir(std::vector<FileWithTime> & fileList,TRACE_TYPE traceType)67 void GetTraceFilesInDir(std::vector<FileWithTime>& fileList, TRACE_TYPE traceType)
68 {
69 struct stat fileStat;
70 for (const auto &entry : std::filesystem::directory_iterator(TRACE_FILE_DEFAULT_DIR)) {
71 if (!entry.is_regular_file()) {
72 continue;
73 }
74 std::string fileName = entry.path().filename().string();
75 if (fileName.substr(0, tracePrefixMap[traceType].size()) == tracePrefixMap[traceType]) {
76 if (stat((TRACE_FILE_DEFAULT_DIR + fileName).c_str(), &fileStat) == 0) {
77 fileList.push_back({fileName, fileStat.st_ctime, static_cast<uint64_t>(fileStat.st_size)});
78 }
79 }
80 }
81 HILOG_INFO(LOG_CORE, "GetTraceFilesInDir fileList size: %{public}d.", static_cast<int>(fileList.size()));
82 std::sort(fileList.begin(), fileList.end(), [](const FileWithTime& a, const FileWithTime& b) {
83 return a.ctime < b.ctime;
84 });
85 }
86
RegenerateTraceFileName(const std::string & fileName,const uint64_t & firstPageTraceTime,const uint64_t & traceDuration)87 std::string RegenerateTraceFileName(const std::string& fileName, const uint64_t& firstPageTraceTime,
88 const uint64_t& traceDuration)
89 {
90 std::string namePrefix;
91 auto index = fileName.find(TRACE_SNAPSHOT_PREFIX);
92 if (index == std::string::npos) {
93 return "";
94 }
95 namePrefix = fileName.substr(0, index);
96 time_t utFirstPageTraceTime;
97 if (!ConvertPageTraceTimeToUtTime(firstPageTraceTime, utFirstPageTraceTime)) {
98 HILOG_ERROR(LOG_CORE,
99 "RegenerateTraceFileName: ConvertPageTraceTimeToUtTime failed. firstPageTraceTime:(%{public}" PRIu64
100 "), utFirstPageTraceTime:(%{public}" PRIu64 ").",
101 firstPageTraceTime, static_cast<uint64_t>(utFirstPageTraceTime));
102 return "";
103 }
104 struct tm timeInfo = {};
105 char timeBuf[TIME_BUFFER_SIZE] = {0};
106 if (localtime_r(&utFirstPageTraceTime, &timeInfo) == nullptr) {
107 HILOG_ERROR(LOG_CORE, "RegenerateTraceFileName: get loacl time failed");
108 return "";
109 }
110 (void)strftime(timeBuf, TIME_BUFFER_SIZE, "%Y%m%d%H%M%S", &timeInfo);
111 std::string newName = namePrefix + TRACE_SNAPSHOT_PREFIX + std::string(timeBuf) + "@" +
112 std::to_string(firstPageTraceTime / S_TO_NS) + "-" + std::to_string(traceDuration) + ".sys";
113 return newName;
114 }
115
GetStartAndEndTraceUtTimeFromFileName(const std::string & fileName,uint64_t & traceStartTime,uint64_t & traceEndTime)116 bool GetStartAndEndTraceUtTimeFromFileName(const std::string& fileName, uint64_t& traceStartTime,
117 uint64_t& traceEndTime)
118 {
119 struct tm timeInfo = {};
120 uint32_t number = 0;
121 auto index = fileName.find(TRACE_SNAPSHOT_PREFIX);
122 if (index == std::string::npos) {
123 return false;
124 }
125 if (sscanf_s(fileName.substr(index, fileName.size() - index).c_str(), "trace_%4d%2d%2d%2d%2d%2d@%*[^-]-%u.sys",
126 &timeInfo.tm_year, &timeInfo.tm_mon, &timeInfo.tm_mday,
127 &timeInfo.tm_hour, &timeInfo.tm_min, &timeInfo.tm_sec,
128 &number) != 7) { // 7 : check sscanf_s return value
129 HILOG_ERROR(LOG_CORE, "sscanf_s failed.");
130 return false;
131 }
132 timeInfo.tm_year -= TIME_INIT;
133 timeInfo.tm_mon -= 1;
134 time_t timestamp = mktime(&timeInfo);
135 if (timestamp == -1) {
136 HILOG_ERROR(LOG_CORE, "mktime failed");
137 return false;
138 }
139 traceStartTime = static_cast<uint64_t>(timestamp);
140 traceEndTime = traceStartTime + static_cast<uint64_t>(number) / S_TO_MS;
141 return true;
142 }
143 }
144
RemoveFile(const std::string & fileName)145 bool RemoveFile(const std::string& fileName)
146 {
147 bool result = false;
148 int fd = open(fileName.c_str(), O_RDONLY | O_NONBLOCK);
149 if (fd == -1) {
150 HILOG_WARN(LOG_CORE, "RemoveFile :: open file failed: %{public}s", fileName.c_str());
151 return result;
152 }
153 if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
154 HILOG_WARN(LOG_CORE, "RemoveFile :: get file lock failed, skip remove: %{public}s", fileName.c_str());
155 close(fd);
156 return result;
157 }
158 if (remove(fileName.c_str()) == 0) {
159 HILOG_INFO(LOG_CORE, "RemoveFile :: Delete %{public}s success.", fileName.c_str());
160 result = true;
161 } else {
162 HILOG_WARN(LOG_CORE, "RemoveFile :: Delete %{public}s failed.", fileName.c_str());
163 }
164 flock(fd, LOCK_UN);
165 close(fd);
166 return result;
167 }
168
GenerateTraceFileName(TRACE_TYPE traceType)169 std::string GenerateTraceFileName(TRACE_TYPE traceType)
170 {
171 // eg: /data/log/hitrace/trace_localtime@boottime.sys
172 std::string name = TRACE_FILE_DEFAULT_DIR;
173 name += tracePrefixMap[traceType];
174 // get localtime
175 time_t currentTime = time(nullptr);
176 struct tm timeInfo = {};
177 char timeBuf[TIME_BUFFER_SIZE] = {0};
178 if (localtime_r(¤tTime, &timeInfo) == nullptr) {
179 HILOG_INFO(LOG_CORE, "GenerateTraceFileName: get loacl time failed");
180 return "";
181 }
182 (void)strftime(timeBuf, TIME_BUFFER_SIZE, "%Y%m%d%H%M%S", &timeInfo);
183 name += std::string(timeBuf);
184 // get boottime
185 struct timespec bts = {0, 0};
186 clock_gettime(CLOCK_BOOTTIME, &bts);
187 name += "@" + std::to_string(bts.tv_sec) + "-" + std::to_string(bts.tv_nsec) + ".sys";
188
189 struct timespec mts = {0, 0};
190 clock_gettime(CLOCK_MONOTONIC, &mts);
191 HILOG_INFO(LOG_CORE, "output trace: %{public}s, boot_time(%{public}" PRId64 "), mono_time(%{public}" PRId64 ").",
192 name.c_str(), static_cast<int64_t>(bts.tv_sec), static_cast<int64_t>(mts.tv_sec));
193 return name;
194 }
195
196 /**
197 * When the SERVICE_MODE is started, clear the remaining trace files in the folder.
198 */
DelSnapshotTraceFile(const int & keepFileCount,std::vector<TraceFileInfo> & traceFileVec)199 void DelSnapshotTraceFile(const int& keepFileCount, std::vector<TraceFileInfo>& traceFileVec)
200 {
201 int vecSize = static_cast<int>(traceFileVec.size());
202 if (traceFileVec.empty() || vecSize <= keepFileCount) {
203 HILOG_INFO(LOG_CORE, "DelSnapshotTraceFile: no trace file need to be deleted.");
204 return;
205 }
206
207 std::sort(traceFileVec.begin(), traceFileVec.end(), [](const TraceFileInfo& a, const TraceFileInfo& b) {
208 return a.traceStartTime < b.traceStartTime;
209 });
210 int deleteFileCnt = vecSize - keepFileCount;
211 for (int i = 0; i < deleteFileCnt; i++) {
212 auto it = traceFileVec.begin();
213 if (!RemoveFile((*it).filename)) {
214 HILOG_INFO(LOG_CORE, "DelSnapshotTraceFile: remove file %{public}s failed.", (*it).filename.c_str());
215 }
216 traceFileVec.erase(it);
217 }
218 }
219
220 /**
221 * open trace file aging mechanism
222 */
DelOldRecordTraceFile(const int & fileLimit)223 void DelOldRecordTraceFile(const int& fileLimit)
224 {
225 size_t traceFileLimit = DEFAULT_TRACE_FILE_LIMIT;
226 if (fileLimit != 0) {
227 traceFileLimit = static_cast<size_t>(fileLimit);
228 }
229 HILOG_INFO(LOG_CORE, "DelOldRecordTraceFile: activate aging mechanism with file limit %{public}zu", traceFileLimit);
230
231 std::vector<FileWithTime> fileList;
232 GetTraceFilesInDir(fileList, TRACE_RECORDING);
233
234 if (fileList.size() <= traceFileLimit) {
235 HILOG_INFO(LOG_CORE, "DelOldRecordTraceFile: no record trace file need be deleted.");
236 return;
237 }
238
239 size_t deleteNum = fileList.size() - traceFileLimit;
240 for (size_t i = 0; i < deleteNum; ++i) {
241 if (remove((TRACE_FILE_DEFAULT_DIR + fileList[i].filename).c_str()) == 0) {
242 HILOG_INFO(LOG_CORE, "DelOldRecordTraceFile: delete first: %{public}s success.",
243 fileList[i].filename.c_str());
244 } else {
245 HILOG_ERROR(LOG_CORE, "DelOldRecordTraceFile: delete first: %{public}s failed, errno: %{public}d.",
246 fileList[i].filename.c_str(), errno);
247 }
248 }
249 }
250
ClearOldTraceFile(std::vector<std::string> & fileLists,const int & fileLimit)251 void ClearOldTraceFile(std::vector<std::string>& fileLists, const int& fileLimit)
252 {
253 if (fileLists.size() == 0) {
254 return;
255 }
256
257 size_t traceFileLimit = DEFAULT_TRACE_FILE_LIMIT;
258 if (fileLimit != 0) {
259 traceFileLimit = static_cast<size_t>(fileLimit);
260 }
261 HILOG_INFO(LOG_CORE, "ClearOldTraceFile: activate aging mechanism with file limit %{public}zu", traceFileLimit);
262
263 if (fileLists.size() > traceFileLimit && access(fileLists[0].c_str(), F_OK) == 0) {
264 if (remove(fileLists[0].c_str()) == 0) {
265 fileLists.erase(fileLists.begin());
266 HILOG_INFO(LOG_CORE, "ClearOldTraceFile: delete first success.");
267 } else {
268 HILOG_ERROR(LOG_CORE, "ClearOldTraceFile: delete first failed, errno: %{public}d.", errno);
269 }
270 }
271 }
272
273 /**
274 * When the raw trace is started, clear the saved_events_format files in the folder.
275 */
DelSavedEventsFormat()276 void DelSavedEventsFormat()
277 {
278 const std::string savedEventsFormatPath = TRACE_FILE_DEFAULT_DIR + TRACE_SAVED_EVENTS_FORMAT;
279 if (access(savedEventsFormatPath.c_str(), F_OK) != 0) {
280 // saved_events_format not exit
281 return;
282 }
283 // saved_events_format exit
284 if (remove(savedEventsFormatPath.c_str()) == 0) {
285 HILOG_INFO(LOG_CORE, "Delete saved_events_format success.");
286 } else {
287 HILOG_ERROR(LOG_CORE, "Delete saved_events_format failed.");
288 }
289 }
290
ClearCacheTraceFileByDuration(std::vector<TraceFileInfo> & cacheFileVec)291 void ClearCacheTraceFileByDuration(std::vector<TraceFileInfo>& cacheFileVec)
292 {
293 if (cacheFileVec.empty()) {
294 HILOG_INFO(LOG_CORE, "ClearCacheTraceFileByDuration: no cache file need to be deleted.");
295 return;
296 }
297 std::sort(cacheFileVec.begin(), cacheFileVec.end(), [](const TraceFileInfo& a, const TraceFileInfo& b) {
298 return a.traceStartTime < b.traceStartTime;
299 });
300 uint64_t currentTime = static_cast<uint64_t>(std::time(nullptr));
301 int index = 0;
302 while (index < static_cast<int>(cacheFileVec.size())) {
303 auto it = cacheFileVec.at(index);
304 if ((it.traceEndTime < currentTime - static_cast<uint64_t>(DEFAULT_TRACE_DURATION)) ||
305 (it.traceEndTime > currentTime)) {
306 if (remove(it.filename.c_str()) == 0) {
307 HILOG_INFO(LOG_CORE, "ClearCacheTraceFileByDuration: delete first: %{public}s success.",
308 it.filename.c_str());
309 } else {
310 HILOG_ERROR(LOG_CORE,
311 "ClearCacheTraceFileByDuration: delete first: %{public}s failed, errno: %{public}d.",
312 it.filename.c_str(), errno);
313 }
314 cacheFileVec.erase(cacheFileVec.begin() + index);
315 } else {
316 ++index;
317 }
318 }
319 }
320
ClearCacheTraceFileBySize(std::vector<TraceFileInfo> & cacheFileVec,const uint64_t & fileSizeLimit)321 void ClearCacheTraceFileBySize(std::vector<TraceFileInfo>& cacheFileVec, const uint64_t& fileSizeLimit)
322 {
323 if (cacheFileVec.empty()) {
324 HILOG_INFO(LOG_CORE, "ClearCacheTraceFileBySize: no cache file need to be deleted.");
325 return;
326 }
327 std::sort(cacheFileVec.begin(), cacheFileVec.end(), [](const TraceFileInfo& a, const TraceFileInfo& b) {
328 return a.traceStartTime < b.traceStartTime;
329 });
330 uint64_t totalCacheFileSize = 0;
331 for (size_t i = 0; i < cacheFileVec.size(); ++i) {
332 totalCacheFileSize += cacheFileVec[i].fileSize;
333 }
334 while (totalCacheFileSize > fileSizeLimit) {
335 if (cacheFileVec.empty()) {
336 HILOG_INFO(LOG_CORE, "ClearCacheTraceFileBySize: cacheFileVec is empty.");
337 return;
338 }
339 auto it = cacheFileVec.begin();
340 HILOG_INFO(LOG_CORE, "filename:%{public}s, fileSizeLimit:(%{public}" PRId64
341 "), totalCacheFileSize:(%{public}" PRId64 "), fileSize:(%{public}" PRId64 ").",
342 (*it).filename.c_str(), fileSizeLimit, totalCacheFileSize, (*it).fileSize);
343 if (remove((*it).filename.c_str()) == 0) {
344 totalCacheFileSize = totalCacheFileSize - (*it).fileSize;
345 HILOG_INFO(LOG_CORE, "ClearCacheTraceFileBySize: delete first %{public}s success.", (*it).filename.c_str());
346 } else {
347 HILOG_ERROR(LOG_CORE, "ClearCacheTraceFileBySize: delete first: %{public}s failed, errno: %{public}d",
348 (*it).filename.c_str(), errno);
349 }
350 cacheFileVec.erase(it);
351 }
352 }
353
GetFileSize(const std::string & filePath,uint64_t & fileSize)354 bool GetFileSize(const std::string& filePath, uint64_t& fileSize)
355 {
356 struct stat fileInfo;
357 int ret = stat(filePath.c_str(), &fileInfo);
358 if (ret != 0) {
359 HILOG_ERROR(LOG_CORE, "Get fileSize failed, %{public}s is not exist, ret is %{public}d.",
360 filePath.c_str(), ret);
361 return false;
362 }
363 fileSize += static_cast<uint64_t>(fileInfo.st_size);
364 HILOG_INFO(LOG_CORE, "GetFileSize: filename:%{public}s, fileSize:(%{public}" PRId64 ").",
365 filePath.c_str(), fileSize);
366 return true;
367 }
368
ConvertPageTraceTimeToUtTime(const uint64_t & pageTraceTime,time_t & utPageTraceTime)369 bool ConvertPageTraceTimeToUtTime(const uint64_t& pageTraceTime, time_t& utPageTraceTime)
370 {
371 struct timespec bts = {0, 0};
372 clock_gettime(CLOCK_BOOTTIME, &bts);
373 uint64_t btNow = static_cast<uint64_t>(bts.tv_sec) + (static_cast<uint64_t>(bts.tv_nsec) != 0 ? 1 : 0);
374 uint64_t utNow = static_cast<uint64_t>(std::time(nullptr));
375 uint64_t utBootTime = utNow - btNow;
376 utPageTraceTime = static_cast<time_t>(utBootTime + pageTraceTime / S_TO_NS);
377 return true;
378 }
379
RenameTraceFile(const std::string & fileName,std::string & newFileName,const uint64_t & firstPageTraceTime,const uint64_t & lastPageTraceTime)380 bool RenameTraceFile(const std::string& fileName, std::string& newFileName,
381 const uint64_t& firstPageTraceTime, const uint64_t& lastPageTraceTime)
382 {
383 if (firstPageTraceTime >= lastPageTraceTime) {
384 HILOG_ERROR(LOG_CORE,
385 "RenameTraceFile: firstPageTraceTime is larger than lastPageTraceTime, firstPageTraceTime:(%{public}"
386 PRIu64 "), lastPageTraceTime:(%{public}" PRIu64 ")",
387 firstPageTraceTime, lastPageTraceTime);
388 return false;
389 }
390 HILOG_INFO(LOG_CORE, "RenameTraceFile: firstPageTraceTime:(%{public}" PRIu64
391 "), lastPageTraceTime:(%{public}" PRIu64 ")", firstPageTraceTime, lastPageTraceTime);
392 uint64_t traceDuration = (lastPageTraceTime - firstPageTraceTime) / MS_TO_NS;
393 newFileName = RegenerateTraceFileName(fileName, firstPageTraceTime, traceDuration).c_str();
394 if (newFileName == "") {
395 HILOG_ERROR(LOG_CORE, "RenameTraceFile: RegenerateTraceFileName failed");
396 return false;
397 }
398 if (rename(fileName.c_str(), newFileName.c_str())) {
399 HILOG_ERROR(LOG_CORE, "RenameTraceFile: rename trace file failed, source file:%{public}s, new file:%{public}s.",
400 fileName.c_str(), newFileName.c_str());
401 return false;
402 }
403 HILOG_INFO(LOG_CORE, "RenameTraceFile: rename trace file success, source file:%{public}s, new file:%{public}s.",
404 fileName.c_str(), newFileName.c_str());
405 return true;
406 }
407
RefreshTraceVec(std::vector<TraceFileInfo> & traceVec,const TRACE_TYPE traceType)408 void RefreshTraceVec(std::vector<TraceFileInfo>& traceVec, const TRACE_TYPE traceType)
409 {
410 traceVec.clear();
411 std::vector<FileWithTime> traceFileList;
412 GetTraceFilesInDir(traceFileList, traceType);
413 for (size_t i = 0; i < traceFileList.size(); i++) {
414 TraceFileInfo traceFileInfo;
415 traceFileInfo.filename = TRACE_FILE_DEFAULT_DIR + traceFileList[i].filename;
416 if (!GetStartAndEndTraceUtTimeFromFileName(traceFileInfo.filename, traceFileInfo.traceStartTime,
417 traceFileInfo.traceEndTime)) {
418 continue;
419 }
420 traceFileInfo.fileSize = traceFileList[i].fileSize;
421 traceVec.push_back(traceFileInfo);
422 HILOG_INFO(LOG_CORE,
423 "RefreshTraceVec::filename:%{public}s, traceStartTime:(%{public}" PRIu64
424 "), traceEndTime:(%{public}" PRIu64 "), filesize:(%{public}" PRIu64 ").",
425 traceFileInfo.filename.c_str(), traceFileInfo.traceStartTime,
426 traceFileInfo.traceEndTime, traceFileInfo.fileSize);
427 }
428 }
429 } // namespace Hitrace
430 } // namespace HiviewDFX
431 } // namespace OHOS
432