1b0e7dd80Sopenharmony_ci/*
2b0e7dd80Sopenharmony_ci * Copyright (C) 2024 Huawei Device Co., Ltd.
3b0e7dd80Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4b0e7dd80Sopenharmony_ci * you may not use this file except in compliance with the License.
5b0e7dd80Sopenharmony_ci * You may obtain a copy of the License at
6b0e7dd80Sopenharmony_ci *
7b0e7dd80Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8b0e7dd80Sopenharmony_ci *
9b0e7dd80Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10b0e7dd80Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11b0e7dd80Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12b0e7dd80Sopenharmony_ci * See the License for the specific language governing permissions and
13b0e7dd80Sopenharmony_ci * limitations under the License.
14b0e7dd80Sopenharmony_ci */
15b0e7dd80Sopenharmony_ci
16b0e7dd80Sopenharmony_ci#include "trace_utils.h"
17b0e7dd80Sopenharmony_ci
18b0e7dd80Sopenharmony_ci#include <cinttypes>
19b0e7dd80Sopenharmony_ci#include <ctime>
20b0e7dd80Sopenharmony_ci#include <dirent.h>
21b0e7dd80Sopenharmony_ci#include <fcntl.h>
22b0e7dd80Sopenharmony_ci#include <filesystem>
23b0e7dd80Sopenharmony_ci#include <sys/file.h>
24b0e7dd80Sopenharmony_ci#include <sys/stat.h>
25b0e7dd80Sopenharmony_ci#include <unistd.h>
26b0e7dd80Sopenharmony_ci
27b0e7dd80Sopenharmony_ci#include "hilog/log.h"
28b0e7dd80Sopenharmony_ci
29b0e7dd80Sopenharmony_cinamespace OHOS {
30b0e7dd80Sopenharmony_cinamespace HiviewDFX {
31b0e7dd80Sopenharmony_cinamespace Hitrace {
32b0e7dd80Sopenharmony_cinamespace {
33b0e7dd80Sopenharmony_ciconst std::string TRACE_DEFAULT_DIR = "/data/log/hitrace/";
34b0e7dd80Sopenharmony_ciconst std::string TRACE_EVENT_FORMT = "saved_events_format";
35b0e7dd80Sopenharmony_ciconst std::string TRACE_SNAPSHOT_PREFIX = "trace_";
36b0e7dd80Sopenharmony_ciconst std::string TRACE_RECORDING_PREFIX = "record_trace_";
37b0e7dd80Sopenharmony_ciconst size_t DEFAULT_TRACE_FILE_LIMIT = 15;
38b0e7dd80Sopenharmony_ci
39b0e7dd80Sopenharmony_cistruct FileWithTime {
40b0e7dd80Sopenharmony_ci    std::string filename;
41b0e7dd80Sopenharmony_ci    time_t ctime;
42b0e7dd80Sopenharmony_ci};
43b0e7dd80Sopenharmony_ci
44b0e7dd80Sopenharmony_civoid RemoveFile(const std::string& fileName)
45b0e7dd80Sopenharmony_ci{
46b0e7dd80Sopenharmony_ci    int fd = open(fileName.c_str(), O_RDONLY | O_NONBLOCK);
47b0e7dd80Sopenharmony_ci    if (fd == -1) {
48b0e7dd80Sopenharmony_ci        HILOG_WARN(LOG_CORE, "RemoveFile :: open file failed: %{public}s", fileName.c_str());
49b0e7dd80Sopenharmony_ci        return;
50b0e7dd80Sopenharmony_ci    }
51b0e7dd80Sopenharmony_ci    if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
52b0e7dd80Sopenharmony_ci        HILOG_WARN(LOG_CORE, "RemoveFile :: get file lock failed, skip remove: %{public}s", fileName.c_str());
53b0e7dd80Sopenharmony_ci        close(fd);
54b0e7dd80Sopenharmony_ci        return;
55b0e7dd80Sopenharmony_ci    }
56b0e7dd80Sopenharmony_ci    if (remove(fileName.c_str()) == 0) {
57b0e7dd80Sopenharmony_ci        HILOG_INFO(LOG_CORE, "RemoveFile :: Delete %{public}s success.", fileName.c_str());
58b0e7dd80Sopenharmony_ci    } else {
59b0e7dd80Sopenharmony_ci        HILOG_WARN(LOG_CORE, "RemoveFile :: Delete %{public}s failed.", fileName.c_str());
60b0e7dd80Sopenharmony_ci    }
61b0e7dd80Sopenharmony_ci    flock(fd, LOCK_UN);
62b0e7dd80Sopenharmony_ci    close(fd);
63b0e7dd80Sopenharmony_ci}
64b0e7dd80Sopenharmony_ci
65b0e7dd80Sopenharmony_civoid GetRecordTraceFilesInDir(std::vector<FileWithTime>& fileList)
66b0e7dd80Sopenharmony_ci{
67b0e7dd80Sopenharmony_ci    struct stat fileStat;
68b0e7dd80Sopenharmony_ci    for (const auto &entry : std::filesystem::directory_iterator(TRACE_DEFAULT_DIR)) {
69b0e7dd80Sopenharmony_ci        if (!entry.is_regular_file()) {
70b0e7dd80Sopenharmony_ci            continue;
71b0e7dd80Sopenharmony_ci        }
72b0e7dd80Sopenharmony_ci        std::string fileName = entry.path().filename().string();
73b0e7dd80Sopenharmony_ci        if (fileName.substr(0, TRACE_RECORDING_PREFIX.size()) == TRACE_RECORDING_PREFIX) {
74b0e7dd80Sopenharmony_ci            if (stat((TRACE_DEFAULT_DIR + fileName).c_str(), &fileStat) == 0) {
75b0e7dd80Sopenharmony_ci                fileList.push_back({fileName, fileStat.st_ctime});
76b0e7dd80Sopenharmony_ci            }
77b0e7dd80Sopenharmony_ci        }
78b0e7dd80Sopenharmony_ci    }
79b0e7dd80Sopenharmony_ci    std::sort(fileList.begin(), fileList.end(), [](const FileWithTime& a, const FileWithTime& b) {
80b0e7dd80Sopenharmony_ci        return a.ctime < b.ctime;
81b0e7dd80Sopenharmony_ci    });
82b0e7dd80Sopenharmony_ci}
83b0e7dd80Sopenharmony_ci}
84b0e7dd80Sopenharmony_ci
85b0e7dd80Sopenharmony_cistd::string GenerateTraceFileName(bool isSnapshot)
86b0e7dd80Sopenharmony_ci{
87b0e7dd80Sopenharmony_ci    // eg: /data/log/hitrace/trace_localtime@boottime.sys
88b0e7dd80Sopenharmony_ci    std::string name = TRACE_DEFAULT_DIR;
89b0e7dd80Sopenharmony_ci
90b0e7dd80Sopenharmony_ci    if (isSnapshot) {
91b0e7dd80Sopenharmony_ci        name += TRACE_SNAPSHOT_PREFIX;
92b0e7dd80Sopenharmony_ci    } else {
93b0e7dd80Sopenharmony_ci        name += TRACE_RECORDING_PREFIX;
94b0e7dd80Sopenharmony_ci    }
95b0e7dd80Sopenharmony_ci
96b0e7dd80Sopenharmony_ci    // get localtime
97b0e7dd80Sopenharmony_ci    time_t currentTime = time(nullptr);
98b0e7dd80Sopenharmony_ci    struct tm timeInfo = {};
99b0e7dd80Sopenharmony_ci    const int bufferSize = 16;
100b0e7dd80Sopenharmony_ci    char timeStr[bufferSize] = {0};
101b0e7dd80Sopenharmony_ci    if (localtime_r(&currentTime, &timeInfo) == nullptr) {
102b0e7dd80Sopenharmony_ci        HILOG_ERROR(LOG_CORE, "Failed to get localtime.");
103b0e7dd80Sopenharmony_ci        return "";
104b0e7dd80Sopenharmony_ci    }
105b0e7dd80Sopenharmony_ci    (void)strftime(timeStr, bufferSize, "%Y%m%d%H%M%S", &timeInfo);
106b0e7dd80Sopenharmony_ci    name += std::string(timeStr);
107b0e7dd80Sopenharmony_ci    // get boottime
108b0e7dd80Sopenharmony_ci    struct timespec bts = {0, 0};
109b0e7dd80Sopenharmony_ci    clock_gettime(CLOCK_BOOTTIME, &bts);
110b0e7dd80Sopenharmony_ci    name += "@" + std::to_string(bts.tv_sec) + "-" + std::to_string(bts.tv_nsec) + ".sys";
111b0e7dd80Sopenharmony_ci
112b0e7dd80Sopenharmony_ci    struct timespec mts = {0, 0};
113b0e7dd80Sopenharmony_ci    clock_gettime(CLOCK_MONOTONIC, &mts);
114b0e7dd80Sopenharmony_ci    HILOG_INFO(LOG_CORE, "output trace: %{public}s, boot_time(%{public}" PRId64 "), mono_time(%{public}" PRId64 ").",
115b0e7dd80Sopenharmony_ci        name.c_str(), static_cast<int64_t>(bts.tv_sec), static_cast<int64_t>(mts.tv_sec));
116b0e7dd80Sopenharmony_ci    return name;
117b0e7dd80Sopenharmony_ci}
118b0e7dd80Sopenharmony_ci
119b0e7dd80Sopenharmony_ci/**
120b0e7dd80Sopenharmony_ci * When the SERVICE_MODE is started, clear the remaining trace files in the folder.
121b0e7dd80Sopenharmony_ci*/
122b0e7dd80Sopenharmony_civoid DelSnapshotTraceFile(const bool deleteSavedFmt, const int keepFileCount)
123b0e7dd80Sopenharmony_ci{
124b0e7dd80Sopenharmony_ci    if (access(TRACE_DEFAULT_DIR.c_str(), F_OK) != 0) {
125b0e7dd80Sopenharmony_ci        return;
126b0e7dd80Sopenharmony_ci    }
127b0e7dd80Sopenharmony_ci    DIR* dirPtr = opendir(TRACE_DEFAULT_DIR.c_str());
128b0e7dd80Sopenharmony_ci    if (dirPtr == nullptr) {
129b0e7dd80Sopenharmony_ci        HILOG_ERROR(LOG_CORE, "Failed to opendir %{public}s.", TRACE_DEFAULT_DIR.c_str());
130b0e7dd80Sopenharmony_ci        return;
131b0e7dd80Sopenharmony_ci    }
132b0e7dd80Sopenharmony_ci    std::vector<FileWithTime> snapshotTraceFiles;
133b0e7dd80Sopenharmony_ci    struct dirent* ptr = nullptr;
134b0e7dd80Sopenharmony_ci    struct stat fileStat;
135b0e7dd80Sopenharmony_ci    while ((ptr = readdir(dirPtr)) != nullptr) {
136b0e7dd80Sopenharmony_ci        if (ptr->d_type == DT_REG) {
137b0e7dd80Sopenharmony_ci            std::string name = std::string(ptr->d_name);
138b0e7dd80Sopenharmony_ci            if (deleteSavedFmt && name.compare(0, TRACE_EVENT_FORMT.size(), TRACE_EVENT_FORMT) == 0) {
139b0e7dd80Sopenharmony_ci                RemoveFile(TRACE_DEFAULT_DIR + name);
140b0e7dd80Sopenharmony_ci                continue;
141b0e7dd80Sopenharmony_ci            }
142b0e7dd80Sopenharmony_ci            if (name.compare(0, TRACE_SNAPSHOT_PREFIX.size(), TRACE_SNAPSHOT_PREFIX) != 0) {
143b0e7dd80Sopenharmony_ci                continue;
144b0e7dd80Sopenharmony_ci            }
145b0e7dd80Sopenharmony_ci            if (stat((TRACE_DEFAULT_DIR + name).c_str(), &fileStat) == 0) {
146b0e7dd80Sopenharmony_ci                snapshotTraceFiles.push_back({name, fileStat.st_ctime});
147b0e7dd80Sopenharmony_ci            }
148b0e7dd80Sopenharmony_ci        }
149b0e7dd80Sopenharmony_ci    }
150b0e7dd80Sopenharmony_ci    closedir(dirPtr);
151b0e7dd80Sopenharmony_ci
152b0e7dd80Sopenharmony_ci    std::sort(snapshotTraceFiles.begin(), snapshotTraceFiles.end(), [](const FileWithTime& a, const FileWithTime& b) {
153b0e7dd80Sopenharmony_ci        return a.ctime < b.ctime;
154b0e7dd80Sopenharmony_ci    });
155b0e7dd80Sopenharmony_ci
156b0e7dd80Sopenharmony_ci    int snapshotTraceFilesSize = static_cast<int>(snapshotTraceFiles.size());
157b0e7dd80Sopenharmony_ci    int deleteFileCnt = snapshotTraceFilesSize - keepFileCount;
158b0e7dd80Sopenharmony_ci    for (int i = 0; i < deleteFileCnt && i < snapshotTraceFilesSize; i++) {
159b0e7dd80Sopenharmony_ci        RemoveFile(TRACE_DEFAULT_DIR + snapshotTraceFiles[i].filename);
160b0e7dd80Sopenharmony_ci    }
161b0e7dd80Sopenharmony_ci}
162b0e7dd80Sopenharmony_ci
163b0e7dd80Sopenharmony_ci/**
164b0e7dd80Sopenharmony_ci * open trace file aging mechanism
165b0e7dd80Sopenharmony_ci */
166b0e7dd80Sopenharmony_civoid DelOldRecordTraceFile(const int& fileLimit)
167b0e7dd80Sopenharmony_ci{
168b0e7dd80Sopenharmony_ci    size_t traceFileLimit = DEFAULT_TRACE_FILE_LIMIT;
169b0e7dd80Sopenharmony_ci    if (fileLimit != 0) {
170b0e7dd80Sopenharmony_ci        traceFileLimit = static_cast<size_t>(fileLimit);
171b0e7dd80Sopenharmony_ci    }
172b0e7dd80Sopenharmony_ci    HILOG_INFO(LOG_CORE, "DelOldRecordTraceFile: activate aging mechanism with file limit %{public}zu", traceFileLimit);
173b0e7dd80Sopenharmony_ci
174b0e7dd80Sopenharmony_ci    std::vector<FileWithTime> fileList;
175b0e7dd80Sopenharmony_ci    GetRecordTraceFilesInDir(fileList);
176b0e7dd80Sopenharmony_ci
177b0e7dd80Sopenharmony_ci    if (fileList.size() <= traceFileLimit) {
178b0e7dd80Sopenharmony_ci        HILOG_INFO(LOG_CORE, "DelOldRecordTraceFile: no record trace file need be deleted.");
179b0e7dd80Sopenharmony_ci        return;
180b0e7dd80Sopenharmony_ci    }
181b0e7dd80Sopenharmony_ci
182b0e7dd80Sopenharmony_ci    size_t deleteNum = fileList.size() - traceFileLimit;
183b0e7dd80Sopenharmony_ci    for (size_t i = 0; i < deleteNum; ++i) {
184b0e7dd80Sopenharmony_ci        if (remove((TRACE_DEFAULT_DIR + fileList[i].filename).c_str()) == 0) {
185b0e7dd80Sopenharmony_ci            HILOG_INFO(LOG_CORE, "DelOldRecordTraceFile: delete first: %{public}s success.",
186b0e7dd80Sopenharmony_ci                fileList[i].filename.c_str());
187b0e7dd80Sopenharmony_ci        } else {
188b0e7dd80Sopenharmony_ci            HILOG_ERROR(LOG_CORE, "DelOldRecordTraceFile: delete first: %{public}s failed, errno: %{public}d.",
189b0e7dd80Sopenharmony_ci                fileList[i].filename.c_str(), errno);
190b0e7dd80Sopenharmony_ci        }
191b0e7dd80Sopenharmony_ci    }
192b0e7dd80Sopenharmony_ci}
193b0e7dd80Sopenharmony_ci
194b0e7dd80Sopenharmony_civoid ClearOldTraceFile(std::vector<std::string>& fileLists, const int& fileLimit)
195b0e7dd80Sopenharmony_ci{
196b0e7dd80Sopenharmony_ci    if (fileLists.size() == 0) {
197b0e7dd80Sopenharmony_ci        return;
198b0e7dd80Sopenharmony_ci    }
199b0e7dd80Sopenharmony_ci
200b0e7dd80Sopenharmony_ci    size_t traceFileLimit = DEFAULT_TRACE_FILE_LIMIT;
201b0e7dd80Sopenharmony_ci    if (fileLimit != 0) {
202b0e7dd80Sopenharmony_ci        traceFileLimit = static_cast<size_t>(fileLimit);
203b0e7dd80Sopenharmony_ci    }
204b0e7dd80Sopenharmony_ci    HILOG_INFO(LOG_CORE, "ClearOldTraceFile: activate aging mechanism with file limit %{public}zu", traceFileLimit);
205b0e7dd80Sopenharmony_ci
206b0e7dd80Sopenharmony_ci    if (fileLists.size() > traceFileLimit && access(fileLists[0].c_str(), F_OK) == 0) {
207b0e7dd80Sopenharmony_ci        if (remove(fileLists[0].c_str()) == 0) {
208b0e7dd80Sopenharmony_ci            fileLists.erase(fileLists.begin());
209b0e7dd80Sopenharmony_ci            HILOG_INFO(LOG_CORE, "ClearOldTraceFile: delete first success.");
210b0e7dd80Sopenharmony_ci        } else {
211b0e7dd80Sopenharmony_ci            HILOG_ERROR(LOG_CORE, "ClearOldTraceFile: delete first failed, errno: %{public}d.", errno);
212b0e7dd80Sopenharmony_ci        }
213b0e7dd80Sopenharmony_ci    }
214b0e7dd80Sopenharmony_ci}
215b0e7dd80Sopenharmony_ci
216b0e7dd80Sopenharmony_ci/**
217b0e7dd80Sopenharmony_ci * When the raw trace is started, clear the saved_events_format files in the folder.
218b0e7dd80Sopenharmony_ci */
219b0e7dd80Sopenharmony_civoid DelSavedEventsFormat()
220b0e7dd80Sopenharmony_ci{
221b0e7dd80Sopenharmony_ci    const std::string savedEventsFormatPath = TRACE_DEFAULT_DIR + TRACE_EVENT_FORMT;
222b0e7dd80Sopenharmony_ci    if (access(savedEventsFormatPath.c_str(), F_OK) != 0) {
223b0e7dd80Sopenharmony_ci        // saved_events_format not exit
224b0e7dd80Sopenharmony_ci        return;
225b0e7dd80Sopenharmony_ci    }
226b0e7dd80Sopenharmony_ci    // saved_events_format exit
227b0e7dd80Sopenharmony_ci    if (remove(savedEventsFormatPath.c_str()) == 0) {
228b0e7dd80Sopenharmony_ci        HILOG_INFO(LOG_CORE, "Delete saved_events_format success.");
229b0e7dd80Sopenharmony_ci    } else {
230b0e7dd80Sopenharmony_ci        HILOG_ERROR(LOG_CORE, "Delete saved_events_format failed.");
231b0e7dd80Sopenharmony_ci    }
232b0e7dd80Sopenharmony_ci}
233b0e7dd80Sopenharmony_ci} // namespace Hitrace
234b0e7dd80Sopenharmony_ci} // namespace HiviewDFX
235b0e7dd80Sopenharmony_ci} // namespace OHOS
236