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