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