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 <algorithm>
16 #include <chrono>
17 #include <sys/stat.h>
18 #include <unistd.h>
19 #include <vector>
20 
21 #include "cpu_collector.h"
22 #include "file_util.h"
23 #include "ffrt.h"
24 #include "hiview_logger.h"
25 #include "hiview_event_report.h"
26 #include "parameter_ex.h"
27 #include "securec.h"
28 #include "string_util.h"
29 #include "trace_utils.h"
30 #include "trace_worker.h"
31 
32 using namespace std::chrono_literals;
33 using OHOS::HiviewDFX::TraceWorker;
34 
35 namespace OHOS {
36 namespace HiviewDFX {
37 namespace {
38 DEFINE_LOG_TAG("UCollectUtil-TraceCollector");
39 const std::string UNIFIED_SHARE_PATH = "/data/log/hiview/unified_collection/trace/share/";
40 const std::string UNIFIED_SPECIAL_PATH = "/data/log/hiview/unified_collection/trace/special/";
41 const std::string UNIFIED_SHARE_TEMP_PATH = UNIFIED_SHARE_PATH + "temp/";
42 const std::string RELIABILITY = "Reliability";
43 const std::string XPERF = "Xperf";
44 const std::string XPOWER = "Xpower";
45 const std::string BETACLUB = "BetaClub";
46 const std::string APP = "APP";
47 const std::string HIVIEW = "Hiview";
48 const std::string FOUNDATION = "Foundation";
49 const std::string OTHER = "Other";
50 const uint32_t UNIFIED_SHARE_COUNTS = 25;
51 const uint32_t UNIFIED_APP_SHARE_COUNTS = 40;
52 const uint32_t UNIFIED_SPECIAL_XPERF = 3;
53 const uint32_t UNIFIED_SPECIAL_RELIABILITY = 3;
54 const uint32_t UNIFIED_SPECIAL_OTHER = 5;
55 constexpr uint32_t READ_MORE_LENGTH = 100 * 1024;
56 const double CPU_LOAD_THRESHOLD = 0.03;
57 const uint32_t MAX_TRY_COUNT = 6;
58 }
59 
60 enum {
61     SHARE = 0,
62     SPECIAL = 1,
63 };
64 
TransCodeToUcError(TraceErrorCode ret)65 UcError TransCodeToUcError(TraceErrorCode ret)
66 {
67     if (CODE_MAP.find(ret) == CODE_MAP.end()) {
68         HIVIEW_LOGE("ErrorCode is not exists.");
69         return UcError::UNSUPPORT;
70     } else {
71         return CODE_MAP.at(ret);
72     }
73 }
74 
75 class CleanPolicy {
76 public:
CleanPolicy(int type)77     explicit CleanPolicy(int type) : type_(type) {}
~CleanPolicy()78     virtual ~CleanPolicy() {}
79     void DoClean();
80 
81 protected:
82     virtual bool IsMine(const std::string &fileName) = 0;
83     virtual uint32_t MyThreshold() = 0;
84 
85 private:
86     void LoadAllFiles(std::vector<std::string> &files);
87     int type_;
88 };
89 
LoadAllFiles(std::vector<std::string> & files)90 void CleanPolicy::LoadAllFiles(std::vector<std::string> &files)
91 {
92     // set path
93     std::string path;
94     if (type_ == SHARE) {
95         path = UNIFIED_SHARE_PATH;
96     } else {
97         path = UNIFIED_SPECIAL_PATH;
98     }
99     // Load all files under the path
100     FileUtil::GetDirFiles(path, files);
101 }
102 
DoClean()103 void CleanPolicy::DoClean()
104 {
105     // Load all files under the path
106     std::vector<std::string> files;
107     LoadAllFiles(files);
108 
109     // Filter files that belong to me
110     std::map<uint64_t, std::vector<std::string>> myFiles;
111     for (const auto &file : files) {
112         if (IsMine(file)) {
113             struct stat fileInfo;
114             stat(file.c_str(), &fileInfo);
115             std::vector<std::string> fileLists;
116             if (myFiles.find(fileInfo.st_mtime) != myFiles.end()) {
117                 fileLists = myFiles[fileInfo.st_mtime];
118                 fileLists.push_back(file);
119                 myFiles[fileInfo.st_mtime] = fileLists;
120             } else {
121                 fileLists.push_back(file);
122                 myFiles.insert(std::pair<uint64_t, std::vector<std::string>>(fileInfo.st_mtime, fileLists));
123             }
124         }
125     }
126 
127     HIVIEW_LOGI("myFiles size : %{public}zu, MyThreshold : %{public}u.", myFiles.size(), MyThreshold());
128 
129     // Clean up old files
130     while (myFiles.size() > MyThreshold()) {
131         for (const auto &file : myFiles.begin()->second) {
132             FileUtil::RemoveFile(file);
133             HIVIEW_LOGI("remove file : %{public}s is deleted.", file.c_str());
134         }
135         myFiles.erase(myFiles.begin());
136     }
137 }
138 
139 class ShareCleanPolicy : public CleanPolicy {
140 public:
ShareCleanPolicy(int type)141     explicit ShareCleanPolicy(int type) : CleanPolicy(type) {}
~ShareCleanPolicy()142     ~ShareCleanPolicy() override {}
143 
144 protected:
IsMine(const std::string & fileName)145     bool IsMine(const std::string &fileName) override
146     {
147         return true;
148     }
149 
MyThreshold()150     uint32_t MyThreshold() override
151     {
152         return UNIFIED_SHARE_COUNTS;
153     }
154 };
155 
156 class AppShareCleanPolicy : public CleanPolicy {
157 public:
AppShareCleanPolicy(int type)158     explicit AppShareCleanPolicy(int type) : CleanPolicy(type) {}
~AppShareCleanPolicy()159     ~AppShareCleanPolicy() override {}
160 
161 protected:
IsMine(const std::string & fileName)162     bool IsMine(const std::string &fileName) override
163     {
164         if (fileName.find("/" + APP) != std::string::npos) {
165             return true;
166         }
167         return false;
168     }
169 
MyThreshold()170     uint32_t MyThreshold() override
171     {
172         return UNIFIED_APP_SHARE_COUNTS;
173     }
174 };
175 
176 class AppSpecialCleanPolicy : public CleanPolicy {
177 public:
AppSpecialCleanPolicy(int type)178     explicit AppSpecialCleanPolicy(int type) : CleanPolicy(type) {}
~AppSpecialCleanPolicy()179     ~AppSpecialCleanPolicy() override {}
180 
181 protected:
IsMine(const std::string & fileName)182     bool IsMine(const std::string &fileName) override
183     {
184         if (fileName.find("/" + APP) != std::string::npos) {
185             return true;
186         }
187         return false;
188     }
189 
MyThreshold()190     uint32_t MyThreshold() override
191     {
192         return 0;
193     }
194 };
195 
196 class SpecialXperfCleanPolicy : public CleanPolicy {
197 public:
SpecialXperfCleanPolicy(int type)198     explicit SpecialXperfCleanPolicy(int type) : CleanPolicy(type) {}
~SpecialXperfCleanPolicy()199     ~SpecialXperfCleanPolicy() override {}
200 
201 protected:
IsMine(const std::string & fileName)202     bool IsMine(const std::string &fileName) override
203     {
204         // check xperf trace
205         size_t posXperf = fileName.find(XPERF);
206         return posXperf != std::string::npos;
207     }
208 
MyThreshold()209     uint32_t MyThreshold() override
210     {
211         return UNIFIED_SPECIAL_XPERF;
212     }
213 };
214 
215 class SpecialReliabilityCleanPolicy : public CleanPolicy {
216 public:
SpecialReliabilityCleanPolicy(int type)217     explicit SpecialReliabilityCleanPolicy(int type) : CleanPolicy(type) {}
~SpecialReliabilityCleanPolicy()218     ~SpecialReliabilityCleanPolicy() override {}
219 
220 protected:
IsMine(const std::string & fileName)221     bool IsMine(const std::string &fileName) override
222     {
223         // check Reliability trace
224         size_t posReliability = fileName.find(RELIABILITY);
225         return posReliability != std::string::npos;
226     }
227 
MyThreshold()228     uint32_t MyThreshold() override
229     {
230         return UNIFIED_SPECIAL_RELIABILITY;
231     }
232 };
233 
234 class SpecialOtherCleanPolicy : public CleanPolicy {
235 public:
SpecialOtherCleanPolicy(int type)236     explicit SpecialOtherCleanPolicy(int type) : CleanPolicy(type) {}
~SpecialOtherCleanPolicy()237     ~SpecialOtherCleanPolicy() override {}
238 
239 protected:
IsMine(const std::string & fileName)240     bool IsMine(const std::string &fileName) override
241     {
242         // check Betaclub and other trace
243         size_t posBeta = fileName.find(BETACLUB);
244         size_t posOther = fileName.find(OTHER);
245         return posBeta != std::string::npos || posOther != std::string::npos;
246     }
247 
MyThreshold()248     uint32_t MyThreshold() override
249     {
250         return UNIFIED_SPECIAL_OTHER;
251     }
252 };
253 
GetCleanPolicy(int type,UCollect::TraceCaller & caller)254 std::shared_ptr<CleanPolicy> GetCleanPolicy(int type, UCollect::TraceCaller &caller)
255 {
256     if (type == SHARE) {
257         if (caller == UCollect::TraceCaller::APP) {
258             return std::make_shared<AppShareCleanPolicy>(type);
259         }
260 
261         return std::make_shared<ShareCleanPolicy>(type);
262     }
263 
264     if (caller == UCollect::TraceCaller::XPERF) {
265         return std::make_shared<SpecialXperfCleanPolicy>(type);
266     }
267 
268     if (caller == UCollect::TraceCaller::RELIABILITY) {
269         return std::make_shared<SpecialReliabilityCleanPolicy>(type);
270     }
271 
272     if (caller == UCollect::TraceCaller::APP) {
273         return std::make_shared<AppSpecialCleanPolicy>(type);
274     }
275     return std::make_shared<SpecialOtherCleanPolicy>(type);
276 }
277 
FileRemove(UCollect::TraceCaller & caller)278 void FileRemove(UCollect::TraceCaller &caller)
279 {
280     std::shared_ptr<CleanPolicy> shareCleaner = GetCleanPolicy(SHARE, caller);
281     std::shared_ptr<CleanPolicy> specialCleaner = GetCleanPolicy(SPECIAL, caller);
282     switch (caller) {
283         case UCollect::TraceCaller::XPOWER:
284         case UCollect::TraceCaller::HIVIEW:
285             shareCleaner->DoClean();
286             break;
287         case UCollect::TraceCaller::RELIABILITY:
288         case UCollect::TraceCaller::XPERF:
289             shareCleaner->DoClean();
290             specialCleaner->DoClean();
291             break;
292         case UCollect::TraceCaller::APP:
293             shareCleaner->DoClean();
294             specialCleaner->DoClean();
295             break;
296         default:
297             specialCleaner->DoClean();
298             break;
299     }
300 }
301 
CheckAndCreateDirectory(const std::string & tmpDirPath)302 void CheckAndCreateDirectory(const std::string &tmpDirPath)
303 {
304     if (!FileUtil::FileExists(tmpDirPath)) {
305         if (FileUtil::ForceCreateDirectory(tmpDirPath, FileUtil::FILE_PERM_775)) {
306             HIVIEW_LOGD("create listener log directory %{public}s succeed.", tmpDirPath.c_str());
307         } else {
308             HIVIEW_LOGE("create listener log directory %{public}s failed.", tmpDirPath.c_str());
309         }
310     }
311 }
312 
CreateMultiDirectory(const std::string & dirPath)313 bool CreateMultiDirectory(const std::string &dirPath)
314 {
315     uint32_t dirPathLen = dirPath.length();
316     if (dirPathLen > PATH_MAX) {
317         return false;
318     }
319     char tmpDirPath[PATH_MAX] = { 0 };
320     for (uint32_t i = 0; i < dirPathLen; ++i) {
321         tmpDirPath[i] = dirPath[i];
322         if (tmpDirPath[i] == '/') {
323             CheckAndCreateDirectory(tmpDirPath);
324         }
325     }
326     return true;
327 }
328 
EnumToString(UCollect::TraceCaller & caller)329 const std::string EnumToString(UCollect::TraceCaller &caller)
330 {
331     switch (caller) {
332         case UCollect::TraceCaller::RELIABILITY:
333             return RELIABILITY;
334         case UCollect::TraceCaller::XPERF:
335             return XPERF;
336         case UCollect::TraceCaller::XPOWER:
337             return XPOWER;
338         case UCollect::TraceCaller::BETACLUB:
339             return BETACLUB;
340         case UCollect::TraceCaller::APP:
341             return APP;
342         case UCollect::TraceCaller::HIVIEW:
343             return HIVIEW;
344         case UCollect::TraceCaller::FOUNDATION:
345             return FOUNDATION;
346         default:
347             return OTHER;
348     }
349 }
350 
GetUnifiedFiles(TraceRetInfo ret,UCollect::TraceCaller & caller)351 std::vector<std::string> GetUnifiedFiles(TraceRetInfo ret, UCollect::TraceCaller &caller)
352 {
353     if (caller == UCollect::TraceCaller::OTHER || caller == UCollect::TraceCaller::BETACLUB) {
354         return GetUnifiedSpecialFiles(ret, caller);
355     }
356     if (caller == UCollect::TraceCaller::XPOWER || caller == UCollect::TraceCaller::HIVIEW ||
357         caller == UCollect::TraceCaller::FOUNDATION) {
358         return GetUnifiedShareFiles(ret, caller);
359     }
360     GetUnifiedSpecialFiles(ret, caller);
361     return GetUnifiedShareFiles(ret, caller);
362 }
363 
CheckCurrentCpuLoad()364 void CheckCurrentCpuLoad()
365 {
366     std::shared_ptr<UCollectUtil::CpuCollector> collector = UCollectUtil::CpuCollector::Create();
367     int32_t pid = getpid();
368     auto collectResult = collector->CollectProcessCpuStatInfo(pid);
369     HIVIEW_LOGI("first get cpu load %{public}f", collectResult.data.cpuLoad);
370     uint32_t retryTime = 0;
371     while (collectResult.data.cpuLoad > CPU_LOAD_THRESHOLD && retryTime < MAX_TRY_COUNT) {
372         ffrt::this_task::sleep_for(5s);
373         collectResult = collector->CollectProcessCpuStatInfo(pid);
374         HIVIEW_LOGI("retry get cpu load %{public}f", collectResult.data.cpuLoad);
375         retryTime++;
376     }
377 }
378 
AddVersionInfoToZipName(const std::string & srcZipPath)379 std::string AddVersionInfoToZipName(const std::string &srcZipPath)
380 {
381     std::string displayVersion = Parameter::GetDisplayVersionStr();
382     std::string versionStr = StringUtil::ReplaceStr(StringUtil::ReplaceStr(displayVersion, "_", "-"), " ", "_");
383     return StringUtil::ReplaceStr(srcZipPath, ".zip", "@" + versionStr + ".zip");
384 }
385 
ZipTraceFile(const std::string & srcSysPath,const std::string & destZipPath)386 void ZipTraceFile(const std::string &srcSysPath, const std::string &destZipPath)
387 {
388     HIVIEW_LOGI("start ZipTraceFile src: %{public}s, dst: %{public}s", srcSysPath.c_str(), destZipPath.c_str());
389     FILE *srcFp = fopen(srcSysPath.c_str(), "rb");
390     if (srcFp == nullptr) {
391         return;
392     }
393     zip_fileinfo zipInfo;
394     errno_t result = memset_s(&zipInfo, sizeof(zipInfo), 0, sizeof(zipInfo));
395     if (result != EOK) {
396         (void)fclose(srcFp);
397         return;
398     }
399     std::string zipFileName = FileUtil::ExtractFileName(destZipPath);
400     zipFile zipFile = zipOpen((UNIFIED_SHARE_TEMP_PATH + zipFileName).c_str(), APPEND_STATUS_CREATE);
401     if (zipFile == nullptr) {
402         HIVIEW_LOGE("zipOpen failed");
403         (void)fclose(srcFp);
404         return;
405     }
406     CheckCurrentCpuLoad();
407     HiviewEventReport::ReportCpuScene("5");
408     std::string sysFileName = FileUtil::ExtractFileName(srcSysPath);
409     zipOpenNewFileInZip(
410         zipFile, sysFileName.c_str(), &zipInfo, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, Z_DEFAULT_COMPRESSION);
411     int errcode = 0;
412     char buf[READ_MORE_LENGTH] = {0};
413     while (!feof(srcFp)) {
414         size_t numBytes = fread(buf, 1, sizeof(buf), srcFp);
415         if (numBytes <= 0) {
416             HIVIEW_LOGE("zip file failed, size is zero");
417             errcode = -1;
418             break;
419         }
420         zipWriteInFileInZip(zipFile, buf, static_cast<unsigned int>(numBytes));
421         if (ferror(srcFp)) {
422             HIVIEW_LOGE("zip file failed: %{public}s, errno: %{public}d.", srcSysPath.c_str(), errno);
423             errcode = -1;
424             break;
425         }
426     }
427     (void)fclose(srcFp);
428     zipCloseFileInZip(zipFile);
429     zipClose(zipFile, nullptr);
430     if (errcode != 0) {
431         return;
432     }
433     std::string destZipPathWithVersion = AddVersionInfoToZipName(destZipPath);
434     FileUtil::RenameFile(UNIFIED_SHARE_TEMP_PATH + zipFileName, destZipPathWithVersion);
435     HIVIEW_LOGI("finish rename file %{public}s", destZipPathWithVersion.c_str());
436 }
437 
CopyFile(const std::string & src,const std::string & dst)438 void CopyFile(const std::string &src, const std::string &dst)
439 {
440     int ret = FileUtil::CopyFile(src, dst);
441     if (ret != 0) {
442         HIVIEW_LOGE("copy file failed, file is %{public}s.", src.c_str());
443     }
444 }
445 
446 /*
447  * apply to xperf, xpower and reliability
448  * trace path eg.:
449  *     /data/log/hiview/unified_collection/trace/share/
450  *     trace_20230906111617@8290-81765922_{device}_{version}.zip
451 */
GetUnifiedShareFiles(TraceRetInfo ret,UCollect::TraceCaller & caller)452 std::vector<std::string> GetUnifiedShareFiles(TraceRetInfo ret, UCollect::TraceCaller &caller)
453 {
454     if (ret.errorCode != TraceErrorCode::SUCCESS) {
455         HIVIEW_LOGE("DumpTrace: failed to dump trace, error code (%{public}d).", ret.errorCode);
456         return {};
457     }
458 
459     if (!FileUtil::FileExists(UNIFIED_SHARE_TEMP_PATH)) {
460         if (!CreateMultiDirectory(UNIFIED_SHARE_TEMP_PATH)) {
461             HIVIEW_LOGE("failed to create multidirectory.");
462             return {};
463         }
464     }
465 
466     std::vector<std::string> files;
467     for (const auto &tracePath : ret.outputFiles) {
468         std::string traceFile = FileUtil::ExtractFileName(tracePath);
469         const std::string destZipPath = UNIFIED_SHARE_PATH + StringUtil::ReplaceStr(traceFile, ".sys", ".zip");
470         const std::string tempDestZipPath = UNIFIED_SHARE_TEMP_PATH + FileUtil::ExtractFileName(destZipPath);
471         const std::string destZipPathWithVersion = AddVersionInfoToZipName(destZipPath);
472         // for zip if the file has not been compressed
473         if (!FileUtil::FileExists(destZipPathWithVersion) && !FileUtil::FileExists(tempDestZipPath)) {
474             // new empty file is used to restore tasks in queue
475             FileUtil::SaveStringToFile(tempDestZipPath, " ", true);
476             UcollectionTask traceTask = [=]() {
477                 ZipTraceFile(tracePath, destZipPath);
478             };
479             TraceWorker::GetInstance().HandleUcollectionTask(traceTask);
480         }
481         files.push_back(destZipPathWithVersion);
482         HIVIEW_LOGI("trace file : %{public}s.", destZipPathWithVersion.c_str());
483     }
484 
485     // file delete
486     FileRemove(caller);
487 
488     return files;
489 }
490 
491 /*
492  * apply to BetaClub and Other Scenes
493  * trace path eg.:
494  * /data/log/hiview/unified_collection/trace/special/BetaClub_trace_20230906111633@8306-299900816.sys
495 */
GetUnifiedSpecialFiles(TraceRetInfo ret,UCollect::TraceCaller & caller)496 std::vector<std::string> GetUnifiedSpecialFiles(TraceRetInfo ret, UCollect::TraceCaller &caller)
497 {
498     if (ret.errorCode != TraceErrorCode::SUCCESS) {
499         HIVIEW_LOGE("Failed to dump trace, error code (%{public}d).", ret.errorCode);
500         return {};
501     }
502 
503     if (!FileUtil::FileExists(UNIFIED_SPECIAL_PATH)) {
504         if (!CreateMultiDirectory(UNIFIED_SPECIAL_PATH)) {
505             HIVIEW_LOGE("Failed to dump trace, error code (%{public}d).", ret.errorCode);
506             return {};
507         }
508     }
509 
510     std::vector<std::string> files;
511     for (const auto &trace : ret.outputFiles) {
512         std::string traceFile = FileUtil::ExtractFileName(trace);
513         const std::string dst = UNIFIED_SPECIAL_PATH + EnumToString(caller) + "_" + traceFile;
514         // for copy if the file has not been copied
515         if (!FileUtil::FileExists(dst)) {
516             UcollectionTask traceTask = [=]() {
517                 CopyFile(trace, dst);
518             };
519             TraceWorker::GetInstance().HandleUcollectionTask(traceTask);
520         }
521         files.push_back(dst);
522         HIVIEW_LOGI("trace file : %{public}s.", dst.c_str());
523     }
524 
525     // file delete
526     FileRemove(caller);
527     return files;
528 }
529 } // HiViewDFX
530 } // OHOS
531