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