• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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_strategy.h"
16 
17 #include <charconv>
18 
19 #include "collect_event.h"
20 #include "event_publish.h"
21 #include "file_util.h"
22 #include "hiview_global.h"
23 #include "hiview_logger.h"
24 #include "hisysevent.h"
25 #include "time_util.h"
26 #include "trace_utils.h"
27 
28 using namespace OHOS::HiviewDFX::Hitrace;
29 namespace OHOS {
30 namespace HiviewDFX {
31 namespace {
32 DEFINE_LOG_TAG("TraceStrategy");
33 constexpr uint32_t MB_TO_KB = 1024;
34 constexpr uint32_t KB_TO_BYTE = 1024;
35 const std::string UNIFIED_SHARE_PATH = "/data/log/hiview/unified_collection/trace/share/";
36 const std::string UNIFIED_SPECIAL_PATH = "/data/log/hiview/unified_collection/trace/special/";
37 const std::string UNIFIED_TELEMETRY_PATH = "/data/log/hiview/unified_collection/trace/telemetry/";
38 const std::string UNIFIED_SHARE_TEMP_PATH = UNIFIED_SHARE_PATH + "temp/";
39 const std::string TELEMETRY_STRATEGY = "TelemetryStrategy";
40 const std::string KEY_ID = "telemetryId";
41 constexpr int32_t FULL_TRACE_DURATION = -1;
42 const uint32_t UNIFIED_SHARE_COUNTS = 25;
43 const uint32_t UNIFIED_TELEMETRY_COUNTS = 20;
44 const uint32_t UNIFIED_APP_SHARE_COUNTS = 40;
45 const uint32_t UNIFIED_SPECIAL_COUNTS = 3;
46 const uint32_t UNIFIED_SPECIAL_OTHER = 5;
47 const uint32_t UNIFIED_SPECIAL_SCREEN = 1;
48 const uint32_t UNIFIED_SPECIAL_BETACLUB = 2;
49 const uint32_t MS_UNIT = 1000;
50 }
51 
DumpTrace(DumpEvent & dumpEvent,TraceRetInfo & traceRetInfo) const52 TraceRet TraceStrategy::DumpTrace(DumpEvent &dumpEvent, TraceRetInfo &traceRetInfo) const
53 {
54     TraceRet ret;
55     dumpEvent.reqDuration = maxDuration_;
56     dumpEvent.reqTime = happenTime_;
57     dumpEvent.execTime = TimeUtil::GenerateTimestamp() / MS_UNIT; // convert execTime into ms unit
58     auto start = std::chrono::steady_clock::now();
59     if (maxDuration_ == FULL_TRACE_DURATION) {
60         ret = TraceStateMachine::GetInstance().DumpTrace(scenario_, 0, happenTime_, traceRetInfo);
61     } else {
62         ret = TraceStateMachine::GetInstance().DumpTrace(scenario_, maxDuration_, happenTime_, traceRetInfo);
63     }
64     if (!ret.IsSuccess()) {
65         HIVIEW_LOGW("scenario_:%{public}d, stateError:%{public}d, codeError:%{public}d", static_cast<int>(scenario_),
66             static_cast<int>(ret.stateError_), ret.codeError_);
67     }
68     auto end = std::chrono::steady_clock::now();
69     dumpEvent.execDuration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
70     dumpEvent.coverDuration = traceRetInfo.coverDuration;
71     dumpEvent.coverRatio = traceRetInfo.coverRatio;
72     dumpEvent.tags = std::move(traceRetInfo.tags);
73     return ret;
74 }
75 
DoClean(const std::string & tracePath,uint32_t threshold,bool hasPrefix)76 void TraceStrategy::DoClean(const std::string &tracePath, uint32_t threshold, bool hasPrefix)
77 {
78     // Load all files under the path
79     std::vector<std::string> files;
80     FileUtil::GetDirFiles(tracePath, files);
81 
82     // Filter files that belong to me
83     std::deque<std::pair<uint64_t, std::string>> filesWithTimes;
84     for (const auto &file : files) {
85         if (!hasPrefix || IsMine(file)) {
86             struct stat fileInfo;
87             stat(file.c_str(), &fileInfo);
88             filesWithTimes.emplace_back(fileInfo.st_mtime, file);
89         }
90     }
91     std::sort(filesWithTimes.begin(), filesWithTimes.end(), [](const auto& a, const auto& b) {
92         return a.first < b.first;
93     });
94     HIVIEW_LOGI("myFiles size : %{public}zu, MyThreshold : %{public}u.", filesWithTimes.size(), threshold);
95 
96     // Clean up old files, new copied file is still working in sub thread now, only can clean old files here
97     while (filesWithTimes.size() > threshold) {
98         FileUtil::RemoveFile(filesWithTimes.front().second);
99         HIVIEW_LOGI("remove file : %{public}s is deleted.", filesWithTimes.front().second.c_str());
100         filesWithTimes.pop_front();
101     }
102 }
103 
DoDump(std::vector<std::string> & outputFile)104 TraceRet TraceDevStrategy::DoDump(std::vector<std::string> &outputFile)
105 {
106     DumpEvent dumpEvent;
107     dumpEvent.caller = caller_;
108     TraceRetInfo traceRetInfo;
109     TraceRet ret = DumpTrace(dumpEvent, traceRetInfo);
110     if (!ret.IsSuccess()) {
111         WriteDumpTraceHisysevent(dumpEvent, GetUcError(ret));
112         return ret;
113     }
114     if (traceRetInfo.outputFiles.empty()) {
115         HIVIEW_LOGE("TraceDevStrategy outputFiles empty.");
116         return ret;
117     }
118     int64_t traceSize = GetTraceSize(traceRetInfo);
119     if (traceSize <= static_cast<int64_t>(INT32_MAX) * MB_TO_KB * KB_TO_BYTE) {
120         dumpEvent.fileSize = traceSize / MB_TO_KB / KB_TO_BYTE;
121     }
122     WriteDumpTraceHisysevent(dumpEvent, UcError::SUCCESS);
123     if (scenario_== TraceScenario::TRACE_COMMAND) {
124         outputFile = traceRetInfo.outputFiles;
125         return ret;
126     }
127     outputFile = GetUnifiedSpecialFiles(traceRetInfo.outputFiles, caller_);
128     if (caller_ == CallerName::SCREEN) {
129         DoClean(UNIFIED_SPECIAL_PATH, UNIFIED_SPECIAL_SCREEN, true);
130     } else if (caller_ == ClientName::BETACLUB) {
131         DoClean(UNIFIED_SPECIAL_PATH, UNIFIED_SPECIAL_BETACLUB, true);
132     } else {
133         DoClean(UNIFIED_SPECIAL_PATH, UNIFIED_SPECIAL_OTHER, true);
134     }
135     return ret;
136 }
137 
IsMine(const std::string & fileName)138 bool TraceDevStrategy::IsMine(const std::string &fileName)
139 {
140     bool result = fileName.find(caller_) != std::string::npos;
141     HIVIEW_LOGI("TraceDevStrategy caller_:%{public}s fileName:%{public}s result:%{public}d", caller_.c_str(),
142         fileName.c_str(), result);
143     return result;
144 }
145 
DoDump(std::vector<std::string> & outputFile)146 TraceRet TraceFlowControlStrategy::DoDump(std::vector<std::string> &outputFile)
147 {
148     if (!flowController_->NeedDump()) {
149         HIVIEW_LOGI("trace is over flow, can not dump.");
150         return TraceRet(TraceFlowCode::TRACE_DUMP_DENY);
151     }
152     DumpEvent dumpEvent;
153     dumpEvent.caller = caller_;
154     TraceRetInfo traceRetInfo;
155     TraceRet ret = DumpTrace(dumpEvent, traceRetInfo);
156     if (!ret.IsSuccess()) {
157         WriteDumpTraceHisysevent(dumpEvent, GetUcError(ret));
158         return ret;
159     }
160     if (traceRetInfo.outputFiles.empty()) {
161         HIVIEW_LOGE("TraceFlowControlStrategy outputFiles empty.");
162         return ret;
163     }
164     int64_t traceSize = GetTraceSize(traceRetInfo);
165     if (traceSize <= static_cast<int64_t>(INT32_MAX) * MB_TO_KB * KB_TO_BYTE) {
166         dumpEvent.fileSize = traceSize / MB_TO_KB / KB_TO_BYTE;
167     }
168     if (!flowController_->NeedUpload(traceSize)) {
169         WriteDumpTraceHisysevent(dumpEvent, TransFlowToUcError(TraceFlowCode::TRACE_UPLOAD_DENY));
170         HIVIEW_LOGI("trace is over flow, can not upload.");
171         return TraceRet(TraceFlowCode::TRACE_UPLOAD_DENY);
172     }
173     outputFile = GetUnifiedZipFiles(traceRetInfo.outputFiles, UNIFIED_SHARE_PATH);
174     DoClean(UNIFIED_SHARE_PATH, UNIFIED_SHARE_COUNTS, false);
175     WriteDumpTraceHisysevent(dumpEvent, UcError::SUCCESS);
176     flowController_->StoreDb();
177     return {};
178 }
179 
IsMine(const std::string & fileName)180 bool TraceFlowControlStrategy::IsMine(const std::string &fileName)
181 {
182     return true;
183 }
184 
DoDump(std::vector<std::string> & outputFile)185 TraceRet TraceMixedStrategy::DoDump(std::vector<std::string> &outputFile)
186 {
187     DumpEvent dumpEvent;
188     dumpEvent.caller = caller_;
189     TraceRetInfo traceRetInfo;
190 
191     // first dump trace in special dir then check flow to decide whether put trace in share dir
192     TraceRet ret = DumpTrace(dumpEvent, traceRetInfo);
193     if (!ret.IsSuccess()) {
194         WriteDumpTraceHisysevent(dumpEvent, GetUcError(ret));
195         return ret;
196     }
197     if (traceRetInfo.outputFiles.empty()) {
198         HIVIEW_LOGE("TraceMixedStrategy outputFiles empty.");
199         return ret;
200     }
201     int64_t traceSize = GetTraceSize(traceRetInfo);
202     outputFile = GetUnifiedSpecialFiles(traceRetInfo.outputFiles, caller_);
203     DoClean(UNIFIED_SPECIAL_PATH, UNIFIED_SPECIAL_COUNTS, true);
204     if (flowController_->NeedDump()) {
205         if (!flowController_->NeedUpload(traceSize)) {
206             WriteDumpTraceHisysevent(dumpEvent, TransFlowToUcError(TraceFlowCode::TRACE_UPLOAD_DENY));
207             HIVIEW_LOGI("over flow, trace generate in specil dir, can not upload.");
208             return {};
209         }
210         outputFile = GetUnifiedZipFiles(traceRetInfo.outputFiles, UNIFIED_SHARE_PATH);
211         DoClean(UNIFIED_SHARE_PATH, UNIFIED_SHARE_COUNTS, false);
212     } else {
213         HIVIEW_LOGI("over flow, trace generate in specil dir, can not upload.");
214         return {};
215     }
216     WriteDumpTraceHisysevent(dumpEvent, UcError::SUCCESS);
217     flowController_->StoreDb();
218     return {};
219 }
220 
IsMine(const std::string & fileName)221 bool TraceMixedStrategy::IsMine(const std::string &fileName)
222 {
223     return fileName.find(caller_) != std::string::npos;
224 }
225 
DoDump(std::vector<std::string> & outputFile)226 TraceRet TelemetryStrategy::DoDump(std::vector<std::string> &outputFile)
227 {
228     TraceRetInfo traceRetInfo;
229     auto ret = TraceStateMachine::GetInstance().DumpTraceWithFilter(pidList_, maxDuration_, happenTime_, traceRetInfo);
230     if (!ret.IsSuccess()) {
231         HIVIEW_LOGE("fail stateError:%{public}d codeError:%{public}d", static_cast<int>(ret.GetStateError()),
232             static_cast<int>(ret.GetCodeError()));
233         return ret;
234     }
235     int64_t traceSize = GetTraceSize(traceRetInfo);
236     if (auto fet = flowController_->NeedTelemetryDump(caller_, traceSize); fet != TelemetryRet::SUCCESS) {
237         HIVIEW_LOGI("trace is over flow, can not dump.");
238         return TraceRet(TraceFlowCode::TRACE_DUMP_DENY);
239     }
240     if (traceRetInfo.outputFiles.empty()) {
241         HIVIEW_LOGW("TraceFlowControlStrategy outputFiles empty.");
242         return TraceRet(traceRetInfo.errorCode);
243     }
244     outputFile = GetUnifiedZipFiles(traceRetInfo.outputFiles, UNIFIED_TELEMETRY_PATH);
245     DoClean(UNIFIED_TELEMETRY_PATH, UNIFIED_TELEMETRY_COUNTS, false);
246     return {};
247 }
248 
DoDump(std::vector<std::string> & outputFile)249 TraceRet TraceAppStrategy::DoDump(std::vector<std::string> &outputFile)
250 {
251     TraceRetInfo traceRetInfo;
252     auto appPid = TraceStateMachine::GetInstance().GetCurrentAppPid();
253     if (appPid != appCallerEvent_->pid_) {
254         HIVIEW_LOGW("pid check fail, maybe is app state closed, current:%{public}d, pid:%{public}d", appPid,
255             appCallerEvent_->pid_);
256         return TraceRet(TraceStateCode::FAIL);
257     }
258     if (flowController_->HasCallOnceToday(appCallerEvent_->uid_, appCallerEvent_->happenTime_)) {
259         HIVIEW_LOGE("already capture trace uid=%{public}d pid==%{public}d", appCallerEvent_->uid_,
260                     appCallerEvent_->pid_);
261         return TraceRet(TraceFlowCode::TRACE_HAS_CAPTURED_TRACE);
262     }
263     TraceRet ret = TraceStateMachine::GetInstance().DumpTrace(scenario_, 0, 0, traceRetInfo);
264     appCallerEvent_->taskBeginTime_ = static_cast<int64_t>(TraceStateMachine::GetInstance().GetTaskBeginTime());
265     appCallerEvent_->taskEndTime_ = static_cast<int64_t>(TimeUtil::GetMilliseconds());
266     if (ret.IsSuccess()) {
267         outputFile = traceRetInfo.outputFiles;
268         if (outputFile.empty() || outputFile[0].empty()) {
269             HIVIEW_LOGE("TraceAppStrategy dump file empty");
270             return ret;
271         }
272         std::string traceFileName = InnerMakeTraceFileName(appCallerEvent_);
273         FileUtil::RenameFile(outputFile[0], traceFileName);
274         appCallerEvent_->externalLog_ = traceFileName;
275         flowController_->RecordCaller(appCallerEvent_);
276     }
277     InnerShareAppEvent(appCallerEvent_);
278     CleanOldAppTrace();
279     InnerReportMainThreadJankForTrace(appCallerEvent_);
280     return ret;
281 }
282 
InnerReportMainThreadJankForTrace(std::shared_ptr<AppCallerEvent> appCallerEvent)283 void TraceAppStrategy::InnerReportMainThreadJankForTrace(std::shared_ptr<AppCallerEvent> appCallerEvent)
284 {
285     HiSysEventWrite(HiSysEvent::Domain::FRAMEWORK, UCollectUtil::MAIN_THREAD_JANK, HiSysEvent::EventType::FAULT,
286         UCollectUtil::SYS_EVENT_PARAM_BUNDLE_NAME, appCallerEvent->bundleName_,
287         UCollectUtil::SYS_EVENT_PARAM_BUNDLE_VERSION, appCallerEvent->bundleVersion_,
288         UCollectUtil::SYS_EVENT_PARAM_BEGIN_TIME, appCallerEvent->beginTime_,
289         UCollectUtil::SYS_EVENT_PARAM_END_TIME, appCallerEvent->endTime_,
290         UCollectUtil::SYS_EVENT_PARAM_THREAD_NAME, appCallerEvent->threadName_,
291         UCollectUtil::SYS_EVENT_PARAM_FOREGROUND, appCallerEvent->foreground_,
292         UCollectUtil::SYS_EVENT_PARAM_LOG_TIME, appCallerEvent->taskEndTime_,
293         UCollectUtil::SYS_EVENT_PARAM_JANK_LEVEL, 1); // 1: over 450ms
294 }
295 
IsMine(const std::string & fileName)296 bool TraceAppStrategy::IsMine(const std::string &fileName)
297 {
298     if (fileName.find("/" + ClientName::APP) != std::string::npos) {
299         return true;
300     }
301     return false;
302 }
303 
CleanOldAppTrace()304 void TraceAppStrategy::CleanOldAppTrace()
305 {
306     DoClean(UNIFIED_SHARE_PATH, UNIFIED_APP_SHARE_COUNTS, true);
307     uint64_t timeNow = TimeUtil::GetMilliseconds() / TimeUtil::SEC_TO_MILLISEC;
308     uint32_t secondsOfThreeDays = 3 * TimeUtil::SECONDS_PER_DAY; // 3 : clean data three days ago
309     if (timeNow < secondsOfThreeDays) {
310         HIVIEW_LOGW("time is invalid");
311         return;
312     }
313     uint64_t timeThreeDaysAgo = timeNow - secondsOfThreeDays;
314     std::string dateThreeDaysAgo = TimeUtil::TimestampFormatToDate(timeThreeDaysAgo, "%Y%m%d");
315     int32_t dateNum = 0;
316     auto result = std::from_chars(dateThreeDaysAgo.c_str(),
317         dateThreeDaysAgo.c_str() + dateThreeDaysAgo.size(), dateNum);
318     if (result.ec != std::errc()) {
319         HIVIEW_LOGW("convert error, dateStr: %{public}s", dateThreeDaysAgo.c_str());
320         return;
321     }
322     flowController_->CleanOldAppTrace(dateNum);
323 }
324 
InnerShareAppEvent(std::shared_ptr<AppCallerEvent> appCallerEvent)325 void TraceAppStrategy::InnerShareAppEvent(std::shared_ptr<AppCallerEvent> appCallerEvent)
326 {
327     Json::Value eventJson;
328     eventJson[UCollectUtil::APP_EVENT_PARAM_UID] = appCallerEvent->uid_;
329     eventJson[UCollectUtil::APP_EVENT_PARAM_PID] = appCallerEvent->pid_;
330     eventJson[UCollectUtil::APP_EVENT_PARAM_TIME] = appCallerEvent->happenTime_;
331     eventJson[UCollectUtil::APP_EVENT_PARAM_BUNDLE_NAME] = appCallerEvent->bundleName_;
332     eventJson[UCollectUtil::APP_EVENT_PARAM_BUNDLE_VERSION] = appCallerEvent->bundleVersion_;
333     eventJson[UCollectUtil::APP_EVENT_PARAM_BEGIN_TIME] = appCallerEvent->beginTime_;
334     eventJson[UCollectUtil::APP_EVENT_PARAM_END_TIME] = appCallerEvent->endTime_;
335     eventJson[UCollectUtil::APP_EVENT_PARAM_ISBUSINESSJANK] = appCallerEvent->isBusinessJank_;
336     Json::Value externalLog;
337     externalLog.append(appCallerEvent->externalLog_);
338     eventJson[UCollectUtil::APP_EVENT_PARAM_EXTERNAL_LOG] = externalLog;
339     std::string param = Json::FastWriter().write(eventJson);
340 
341     HIVIEW_LOGI("send for uid=%{public}d pid=%{public}d", appCallerEvent->uid_, appCallerEvent->pid_);
342     EventPublish::GetInstance().PushEvent(appCallerEvent->uid_, UCollectUtil::MAIN_THREAD_JANK,
343                                           HiSysEvent::EventType::FAULT, param);
344 }
345 
InnerMakeTraceFileName(std::shared_ptr<AppCallerEvent> appCallerEvent)346 std::string TraceAppStrategy::InnerMakeTraceFileName(std::shared_ptr<AppCallerEvent> appCallerEvent)
347 {
348     std::string &bundleName = appCallerEvent->bundleName_;
349     int32_t pid = appCallerEvent->pid_;
350     int64_t beginTime = appCallerEvent->taskBeginTime_;
351     int64_t endTime = appCallerEvent->taskEndTime_;
352     int32_t costTime = (appCallerEvent->taskEndTime_ - appCallerEvent->taskBeginTime_);
353 
354     std::string d1 = TimeUtil::TimestampFormatToDate(beginTime/ TimeUtil::SEC_TO_MILLISEC, "%Y%m%d%H%M%S");
355     std::string d2 = TimeUtil::TimestampFormatToDate(endTime/ TimeUtil::SEC_TO_MILLISEC, "%Y%m%d%H%M%S");
356 
357     std::string name;
358     name.append(UNIFIED_SHARE_PATH).append("APP_").append(bundleName).append("_").append(std::to_string(pid));
359     name.append("_").append(d1).append("_").append(d2).append("_").append(std::to_string(costTime)).append(".sys");
360     return name;
361 }
362 
CreateTraceStrategy(UCollect::TraceCaller caller,int32_t maxDuration,uint64_t happenTime)363 std::shared_ptr<TraceStrategy> TraceFactory::CreateTraceStrategy(UCollect::TraceCaller caller, int32_t maxDuration,
364     uint64_t happenTime)
365 {
366     switch (caller) {
367         case UCollect::TraceCaller::XPERF:
368         case UCollect::TraceCaller::RELIABILITY:
369             return std::make_shared<TraceMixedStrategy>(maxDuration, happenTime, EnumToString(caller));
370         case UCollect::TraceCaller::XPOWER:
371         case UCollect::TraceCaller::HIVIEW:
372             return std::make_shared<TraceFlowControlStrategy>(maxDuration, happenTime, EnumToString(caller));
373         case UCollect::TraceCaller::OTHER:
374         case UCollect::TraceCaller::SCREEN:
375             return std::make_shared<TraceDevStrategy>(maxDuration, happenTime, EnumToString(caller),
376                 TraceScenario::TRACE_COMMON);
377         default:
378             return nullptr;
379     }
380 }
381 
CreateTraceStrategy(UCollect::TraceClient client,int32_t maxDuration,uint64_t happenTime)382 std::shared_ptr<TraceStrategy> TraceFactory::CreateTraceStrategy(UCollect::TraceClient client, int32_t maxDuration,
383     uint64_t happenTime)
384 {
385     switch (client) {
386         case UCollect::TraceClient::COMMAND:
387             return std::make_shared<TraceDevStrategy>(maxDuration, happenTime, ClientToString(client),
388                 TraceScenario::TRACE_COMMAND);
389         case UCollect::TraceClient::COMMON_DEV:
390         case UCollect::TraceClient::BETACLUB:
391             return std::make_shared<TraceDevStrategy>(maxDuration, happenTime, ClientToString(client),
392                 TraceScenario::TRACE_COMMON);
393         default:
394             return nullptr;
395     }
396 }
397 
CreateAppStrategy(std::shared_ptr<AppCallerEvent> appCallerEvent)398 std::shared_ptr<TraceAppStrategy> TraceFactory::CreateAppStrategy(std::shared_ptr<AppCallerEvent> appCallerEvent)
399 {
400     return std::make_shared<TraceAppStrategy>(appCallerEvent);
401 }
402 }
403 }
404