1 /*
2 * Copyright (c) 2023-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 #include "trace_utils.h"
16
17 #include <algorithm>
18 #include <chrono>
19 #include <contrib/minizip/zip.h>
20 #include <fcntl.h>
21 #include <sys/file.h>
22 #include <unistd.h>
23 #include <vector>
24
25 #include "cpu_collector.h"
26 #include "file_util.h"
27 #include "ffrt.h"
28 #include "hiview_logger.h"
29 #include "hiview_event_report.h"
30 #include "memory_collector.h"
31 #include "parameter_ex.h"
32 #include "securec.h"
33 #include "string_util.h"
34 #include "trace_common.h"
35 #include "trace_worker.h"
36 #include "time_util.h"
37
38 using namespace std::chrono_literals;
39 using OHOS::HiviewDFX::TraceWorker;
40
41 namespace OHOS {
42 namespace HiviewDFX {
43 namespace {
44 DEFINE_LOG_TAG("UCollectUtil-TraceCollector");
45 const std::string UNIFIED_SHARE_PATH = "/data/log/hiview/unified_collection/trace/share/";
46 const std::string UNIFIED_SPECIAL_PATH = "/data/log/hiview/unified_collection/trace/special/";
47 const std::string UNIFIED_TELEMETRY_PATH = "/data/log/hiview/unified_collection/trace/telemetry/";
48 const std::string UNIFIED_SHARE_TEMP_PATH = UNIFIED_SHARE_PATH + "temp/";
49 constexpr uint32_t READ_MORE_LENGTH = 100 * 1024;
50 const double CPU_LOAD_THRESHOLD = 0.03;
51 const uint32_t MAX_TRY_COUNT = 6;
52 constexpr uint32_t MB_TO_KB = 1024;
53 constexpr uint32_t KB_TO_BYTE = 1024;
54 }
55
TransCodeToUcError(TraceErrorCode ret)56 UcError TransCodeToUcError(TraceErrorCode ret)
57 {
58 if (CODE_MAP.find(ret) == CODE_MAP.end()) {
59 HIVIEW_LOGE("ErrorCode is not exists.");
60 return UcError::UNSUPPORT;
61 } else {
62 return CODE_MAP.at(ret);
63 }
64 }
65
TransStateToUcError(TraceStateCode ret)66 UcError TransStateToUcError(TraceStateCode ret)
67 {
68 if (TRACE_STATE_MAP.find(ret) == TRACE_STATE_MAP.end()) {
69 HIVIEW_LOGE("ErrorCode is not exists.");
70 return UcError::UNSUPPORT;
71 } else {
72 return TRACE_STATE_MAP.at(ret);
73 }
74 }
75
TransFlowToUcError(TraceFlowCode ret)76 UcError TransFlowToUcError(TraceFlowCode ret)
77 {
78 if (TRACE_FLOW_MAP.find(ret) == TRACE_FLOW_MAP.end()) {
79 HIVIEW_LOGE("ErrorCode is not exists.");
80 return UcError::UNSUPPORT;
81 } else {
82 return TRACE_FLOW_MAP.at(ret);
83 }
84 }
85
ModuleToString(UCollect::TeleModule & module)86 const std::string ModuleToString(UCollect::TeleModule &module)
87 {
88 switch (module) {
89 case UCollect::TeleModule::XPERF:
90 return CallerName::XPERF;
91 case UCollect::TeleModule::XPOWER:
92 return CallerName::XPOWER;
93 default:
94 return "";
95 }
96 }
97
EnumToString(UCollect::TraceCaller & caller)98 const std::string EnumToString(UCollect::TraceCaller &caller)
99 {
100 switch (caller) {
101 case UCollect::TraceCaller::RELIABILITY:
102 return CallerName::RELIABILITY;
103 case UCollect::TraceCaller::XPERF:
104 return CallerName::XPERF;
105 case UCollect::TraceCaller::XPOWER:
106 return CallerName::XPOWER;
107 case UCollect::TraceCaller::HIVIEW:
108 return CallerName::HIVIEW;
109 case UCollect::TraceCaller::OTHER:
110 return CallerName::OTHER;
111 case UCollect::TraceCaller::SCREEN:
112 return CallerName::SCREEN;
113 default:
114 return "";
115 }
116 }
117
ClientToString(UCollect::TraceClient & client)118 const std::string ClientToString(UCollect::TraceClient &client)
119 {
120 switch (client) {
121 case UCollect::TraceClient::COMMAND:
122 return ClientName::COMMAND;
123 case UCollect::TraceClient::COMMON_DEV:
124 return ClientName::COMMON_DEV;
125 case UCollect::TraceClient::BETACLUB:
126 return ClientName::BETACLUB;
127 default:
128 return "";
129 }
130 }
131
CheckCurrentCpuLoad()132 void CheckCurrentCpuLoad()
133 {
134 std::shared_ptr<UCollectUtil::CpuCollector> collector = UCollectUtil::CpuCollector::Create();
135 int32_t pid = getpid();
136 auto collectResult = collector->CollectProcessCpuStatInfo(pid);
137 HIVIEW_LOGI("first get cpu load %{public}f", collectResult.data.cpuLoad);
138 uint32_t retryTime = 0;
139 while (collectResult.data.cpuLoad > CPU_LOAD_THRESHOLD && retryTime < MAX_TRY_COUNT) {
140 ffrt::this_task::sleep_for(5s);
141 collectResult = collector->CollectProcessCpuStatInfo(pid);
142 HIVIEW_LOGI("retry get cpu load %{public}f", collectResult.data.cpuLoad);
143 retryTime++;
144 }
145 }
146
AddVersionInfoToZipName(const std::string & srcZipPath)147 std::string AddVersionInfoToZipName(const std::string &srcZipPath)
148 {
149 std::string displayVersion = Parameter::GetDisplayVersionStr();
150 std::string versionStr = StringUtil::ReplaceStr(StringUtil::ReplaceStr(displayVersion, "_", "-"), " ", "_");
151 return StringUtil::ReplaceStr(srcZipPath, ".zip", "@" + versionStr + ".zip");
152 }
153
ZipTraceFile(const std::string & srcSysPath,const std::string & destZipPath)154 void ZipTraceFile(const std::string &srcSysPath, const std::string &destZipPath)
155 {
156 HIVIEW_LOGI("start ZipTraceFile src: %{public}s, dst: %{public}s", srcSysPath.c_str(), destZipPath.c_str());
157 FILE *srcFp = fopen(srcSysPath.c_str(), "rb");
158 if (srcFp == nullptr) {
159 return;
160 }
161 zip_fileinfo zipInfo;
162 errno_t result = memset_s(&zipInfo, sizeof(zipInfo), 0, sizeof(zipInfo));
163 if (result != EOK) {
164 (void)fclose(srcFp);
165 return;
166 }
167 std::string zipFileName = FileUtil::ExtractFileName(destZipPath);
168 zipFile zipFile = zipOpen((UNIFIED_SHARE_TEMP_PATH + zipFileName).c_str(), APPEND_STATUS_CREATE);
169 if (zipFile == nullptr) {
170 HIVIEW_LOGE("zipOpen failed");
171 (void)fclose(srcFp);
172 return;
173 }
174 CheckCurrentCpuLoad();
175 HiviewEventReport::ReportCpuScene("5");
176 std::string sysFileName = FileUtil::ExtractFileName(srcSysPath);
177 zipOpenNewFileInZip(
178 zipFile, sysFileName.c_str(), &zipInfo, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, Z_DEFAULT_COMPRESSION);
179 int errcode = 0;
180 char buf[READ_MORE_LENGTH] = {0};
181 while (!feof(srcFp)) {
182 size_t numBytes = fread(buf, 1, sizeof(buf), srcFp);
183 if (numBytes <= 0) {
184 HIVIEW_LOGE("zip file failed, size is zero");
185 errcode = -1;
186 break;
187 }
188 zipWriteInFileInZip(zipFile, buf, static_cast<unsigned int>(numBytes));
189 if (ferror(srcFp)) {
190 HIVIEW_LOGE("zip file failed: %{public}s, errno: %{public}d.", srcSysPath.c_str(), errno);
191 errcode = -1;
192 break;
193 }
194 }
195 (void)fclose(srcFp);
196 zipCloseFileInZip(zipFile);
197 zipClose(zipFile, nullptr);
198 if (errcode != 0) {
199 return;
200 }
201 std::string destZipPathWithVersion = AddVersionInfoToZipName(destZipPath);
202 FileUtil::RenameFile(UNIFIED_SHARE_TEMP_PATH + zipFileName, destZipPathWithVersion);
203 HIVIEW_LOGI("finish rename file %{public}s", destZipPathWithVersion.c_str());
204 }
205
CopyFile(const std::string & src,const std::string & dst)206 void CopyFile(const std::string &src, const std::string &dst)
207 {
208 int ret = FileUtil::CopyFile(src, dst);
209 if (ret != 0) {
210 HIVIEW_LOGE("copy file failed, file is %{public}s.", src.c_str());
211 }
212 HIVIEW_LOGI("copy end, trace file : %{public}s.", dst.c_str());
213 }
214
215 /*
216 * apply to xperf, xpower and reliability
217 * trace path eg.:
218 * /data/log/hiview/unified_collection/trace/share/
219 * trace_20230906111617@8290-81765922_{device}_{version}.zip
220 */
GetUnifiedZipFiles(const std::vector<std::string> outputFiles,const std::string & destDir)221 std::vector<std::string> GetUnifiedZipFiles(const std::vector<std::string> outputFiles, const std::string &destDir)
222 {
223 if (!FileUtil::FileExists(UNIFIED_SHARE_TEMP_PATH)) {
224 if (!CreateMultiDirectory(UNIFIED_SHARE_TEMP_PATH)) {
225 HIVIEW_LOGE("failed to create multidirectory.");
226 return {};
227 }
228 }
229
230 std::vector<std::string> files;
231 for (const auto &tracePath : outputFiles) {
232 std::string traceFile = FileUtil::ExtractFileName(tracePath);
233 const std::string destZipPath = destDir + StringUtil::ReplaceStr(traceFile, ".sys", ".zip");
234 const std::string tempDestZipPath = UNIFIED_SHARE_TEMP_PATH + FileUtil::ExtractFileName(destZipPath);
235 const std::string destZipPathWithVersion = AddVersionInfoToZipName(destZipPath);
236 // for zip if the file has not been compressed
237 if (!FileUtil::FileExists(destZipPathWithVersion) && !FileUtil::FileExists(tempDestZipPath)) {
238 // new empty file is used to restore tasks in queue
239 FileUtil::SaveStringToFile(tempDestZipPath, " ", true);
240 UcollectionTask traceTask = [=]() {
241 ZipTraceFile(tracePath, destZipPath);
242 };
243 TraceWorker::GetInstance().HandleUcollectionTask(traceTask);
244 }
245 files.push_back(destZipPathWithVersion);
246 HIVIEW_LOGI("trace file : %{public}s.", destZipPathWithVersion.c_str());
247 }
248 return files;
249 }
250
251 /*
252 * apply to BetaClub and Other Scenes
253 * trace path eg.:
254 * /data/log/hiview/unified_collection/trace/special/BetaClub_trace_20230906111633@8306-299900816.sys
255 */
GetUnifiedSpecialFiles(const std::vector<std::string> & outputFiles,const std::string & prefix)256 std::vector<std::string> GetUnifiedSpecialFiles(const std::vector<std::string>& outputFiles, const std::string& prefix)
257 {
258 if (!FileUtil::FileExists(UNIFIED_SPECIAL_PATH)) {
259 if (!CreateMultiDirectory(UNIFIED_SPECIAL_PATH)) {
260 HIVIEW_LOGE("create dir %{public}s fail", UNIFIED_SPECIAL_PATH.c_str());
261 return {};
262 }
263 }
264
265 std::vector<std::string> files;
266 for (const auto &trace : outputFiles) {
267 std::string traceFile = FileUtil::ExtractFileName(trace);
268 const std::string dst = UNIFIED_SPECIAL_PATH + prefix + "_" + traceFile;
269 files.push_back(dst);
270 // copy trace immediately for betaclub and screen recording
271 if (prefix == CallerName::SCREEN || prefix == ClientName::BETACLUB) {
272 CopyFile(trace, dst);
273 continue;
274 }
275 if (!FileUtil::FileExists(dst)) {
276 // copy trace in ffrt asynchronously
277 UcollectionTask traceTask = [=]() {
278 CopyFile(trace, dst);
279 };
280 TraceWorker::GetInstance().HandleUcollectionTask(traceTask);
281 }
282 }
283 return files;
284 }
285
GetTraceSize(TraceRetInfo & ret)286 int64_t GetTraceSize(TraceRetInfo &ret)
287 {
288 struct stat fileInfo;
289 int64_t traceSize = 0;
290 for (const auto &tracePath : ret.outputFiles) {
291 int ret = stat(tracePath.c_str(), &fileInfo);
292 if (ret != 0) {
293 HIVIEW_LOGE("%{public}s is not exists, ret = %{public}d.", tracePath.c_str(), ret);
294 continue;
295 }
296 traceSize += fileInfo.st_size;
297 }
298 return traceSize;
299 }
300
WriteDumpTraceHisysevent(DumpEvent & dumpEvent,int32_t retCode)301 void WriteDumpTraceHisysevent(DumpEvent &dumpEvent, int32_t retCode)
302 {
303 LoadMemoryInfo(dumpEvent);
304 dumpEvent.errorCode = retCode;
305 int ret = HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::PROFILER, "DUMP_TRACE",
306 OHOS::HiviewDFX::HiSysEvent::EventType::BEHAVIOR,
307 "CALLER", dumpEvent.caller,
308 "ERROR_CODE", dumpEvent.errorCode,
309 "IPC_TIME", dumpEvent.ipcTime,
310 "REQ_TIME", dumpEvent.reqTime,
311 "REQ_DURATION", dumpEvent.reqDuration,
312 "EXEC_TIME", dumpEvent.execTime,
313 "EXEC_DURATION", dumpEvent.execDuration,
314 "COVER_DURATION", dumpEvent.coverDuration,
315 "COVER_RATIO", dumpEvent.coverRatio,
316 "TAGS", dumpEvent.tags,
317 "FILE_SIZE", dumpEvent.fileSize,
318 "SYS_MEM_TOTAL", dumpEvent.sysMemTotal,
319 "SYS_MEM_FREE", dumpEvent.sysMemFree,
320 "SYS_MEM_AVAIL", dumpEvent.sysMemAvail,
321 "SYS_CPU", dumpEvent.sysCpu);
322 if (ret != 0) {
323 HIVIEW_LOGE("HiSysEventWrite failed, ret is %{public}d", ret);
324 }
325 }
326
LoadMemoryInfo(DumpEvent & dumpEvent)327 void LoadMemoryInfo(DumpEvent &dumpEvent)
328 {
329 std::shared_ptr<UCollectUtil::MemoryCollector> collector = UCollectUtil::MemoryCollector::Create();
330 CollectResult<SysMemory> data = collector->CollectSysMemory();
331 dumpEvent.sysMemTotal = data.data.memTotal / MB_TO_KB;
332 dumpEvent.sysMemFree = data.data.memFree / MB_TO_KB;
333 dumpEvent.sysMemAvail = data.data.memAvailable / MB_TO_KB;
334 }
335
GetUcError(TraceRet ret)336 UcError GetUcError(TraceRet ret)
337 {
338 if (ret.stateError_ != TraceStateCode::SUCCESS) {
339 return TransStateToUcError(ret.stateError_);
340 } else if (ret.codeError_!= TraceErrorCode::SUCCESS) {
341 return TransCodeToUcError(ret.codeError_);
342 } else if (ret.flowError_ != TraceFlowCode::TRACE_ALLOW) {
343 return TransFlowToUcError(ret.flowError_);
344 } else {
345 return UcError::SUCCESS;
346 }
347 }
348
CheckAndCreateDirectory(const std::string & tmpDirPath)349 void CheckAndCreateDirectory(const std::string &tmpDirPath)
350 {
351 if (!FileUtil::FileExists(tmpDirPath)) {
352 if (FileUtil::ForceCreateDirectory(tmpDirPath, FileUtil::FILE_PERM_775)) {
353 HIVIEW_LOGD("create listener log directory %{public}s succeed.", tmpDirPath.c_str());
354 } else {
355 HIVIEW_LOGE("create listener log directory %{public}s failed.", tmpDirPath.c_str());
356 }
357 }
358 }
359
CreateMultiDirectory(const std::string & dirPath)360 bool CreateMultiDirectory(const std::string &dirPath)
361 {
362 uint32_t dirPathLen = dirPath.length();
363 if (dirPathLen > PATH_MAX) {
364 return false;
365 }
366 char tmpDirPath[PATH_MAX] = { 0 };
367 for (uint32_t i = 0; i < dirPathLen; ++i) {
368 tmpDirPath[i] = dirPath[i];
369 if (tmpDirPath[i] == '/') {
370 CheckAndCreateDirectory(tmpDirPath);
371 }
372 }
373 return true;
374 }
375
RecoverTmpTrace()376 void RecoverTmpTrace()
377 {
378 std::vector<std::string> traceFiles;
379 FileUtil::GetDirFiles(UNIFIED_SHARE_TEMP_PATH, traceFiles, false);
380 HIVIEW_LOGI("traceFiles need recover: %{public}zu", traceFiles.size());
381 for (auto &filePath : traceFiles) {
382 std::string fileName = FileUtil::ExtractFileName(filePath);
383 HIVIEW_LOGI("unfinished trace file: %{public}s", fileName.c_str());
384 std::string originTraceFile = StringUtil::ReplaceStr("/data/log/hitrace/" + fileName, ".zip", ".sys");
385 if (!FileUtil::FileExists(originTraceFile)) {
386 HIVIEW_LOGI("source file not exist: %{public}s", originTraceFile.c_str());
387 FileUtil::RemoveFile(UNIFIED_SHARE_TEMP_PATH + fileName);
388 continue;
389 }
390 int fd = open(originTraceFile.c_str(), O_RDONLY | O_NONBLOCK);
391 if (fd == -1) {
392 HIVIEW_LOGI("open source file failed: %{public}s", originTraceFile.c_str());
393 continue;
394 }
395 // add lock before zip trace file, in case hitrace delete origin trace file.
396 if (flock(fd, LOCK_EX | LOCK_NB) < 0) {
397 HIVIEW_LOGI("get source file lock failed: %{public}s", originTraceFile.c_str());
398 close(fd);
399 continue;
400 }
401 HIVIEW_LOGI("originTraceFile path: %{public}s", originTraceFile.c_str());
402 UcollectionTask traceTask = [=]() {
403 ZipTraceFile(originTraceFile, UNIFIED_SHARE_PATH + fileName);
404 flock(fd, LOCK_UN);
405 close(fd);
406 };
407 TraceWorker::GetInstance().HandleUcollectionTask(traceTask);
408 }
409 }
410 } // HiViewDFX
411 } // OHOS
412