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