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