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