• 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 "event_publish.h"
20 #include "hiview_logger.h"
21 #include "time_util.h"
22 #include "trace_utils.h"
23 #include "collect_event.h"
24 #include "json/json.h"
25 #include "memory_collector.h"
26 #ifndef TRACE_STRATEGY_UNITTEST
27 #include "trace_state_machine.h"
28 #else
29 #include "test_trace_state_machine.h"
30 #endif
31 
32 namespace OHOS::HiviewDFX {
33 namespace {
34 DEFINE_LOG_TAG("UCollectUtil-TraceCollector");
35 const uint32_t BYTE_UNIT = 1024;
36 constexpr int32_t FULL_TRACE_DURATION = -1;
37 const uint32_t MS_UNIT = 1000;
38 
InitDumpEvent(DumpEvent & dumpEvent,const std::string & caller,uint32_t maxDuration,uint64_t happenTime)39 void InitDumpEvent(DumpEvent &dumpEvent, const std::string &caller, uint32_t maxDuration, uint64_t happenTime)
40 {
41     dumpEvent.caller = caller;
42     dumpEvent.reqDuration = static_cast<int32_t>(maxDuration);
43     dumpEvent.reqTime = happenTime;
44     dumpEvent.execTime = TimeUtil::GenerateTimestamp() / MS_UNIT; // convert execTime into ms unit
45     dumpEvent.startTime = TimeUtil::GetSteadyClockTimeMs();
46 }
47 
UpdateDumpEvent(DumpEvent & dumpEvent,const TraceRet & ret,const TraceRetInfo & retInfo)48 void UpdateDumpEvent(DumpEvent &dumpEvent, const TraceRet &ret, const TraceRetInfo &retInfo)
49 {
50     auto endTime = TimeUtil::GetSteadyClockTimeMs();
51     dumpEvent.errorCode = GetUcError(ret);
52     dumpEvent.execDuration = static_cast<int32_t>(endTime - dumpEvent.startTime);
53     dumpEvent.coverDuration = retInfo.coverDuration;
54     dumpEvent.coverRatio = retInfo.coverRatio;
55     dumpEvent.traceMode = retInfo.mode;
56     dumpEvent.tags = retInfo.tags;
57 }
58 
LoadMemoryInfo(DumpEvent & dumpEvent)59 void LoadMemoryInfo(DumpEvent &dumpEvent)
60 {
61     std::shared_ptr<UCollectUtil::MemoryCollector> collector = UCollectUtil::MemoryCollector::Create();
62     CollectResult<SysMemory> data = collector->CollectSysMemory();
63     dumpEvent.sysMemTotal = data.data.memTotal / BYTE_UNIT;
64     dumpEvent.sysMemFree = data.data.memFree / BYTE_UNIT;
65     dumpEvent.sysMemAvail = data.data.memAvailable / BYTE_UNIT;
66 }
67 
WriteDumpTraceHisysevent(DumpEvent & dumpEvent)68 void WriteDumpTraceHisysevent(DumpEvent &dumpEvent)
69 {
70     LoadMemoryInfo(dumpEvent);
71     int ret = HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::PROFILER, "DUMP_TRACE",
72         OHOS::HiviewDFX::HiSysEvent::EventType::BEHAVIOR,
73         "CALLER", dumpEvent.caller,
74         "ERROR_CODE", dumpEvent.errorCode,
75         "IPC_TIME", dumpEvent.ipcTime,
76         "REQ_TIME", dumpEvent.reqTime,
77         "REQ_DURATION", dumpEvent.reqDuration,
78         "EXEC_TIME", dumpEvent.execTime,
79         "EXEC_DURATION", dumpEvent.execDuration,
80         "COVER_DURATION", dumpEvent.coverDuration,
81         "COVER_RATIO", dumpEvent.coverRatio,
82         "TAGS", dumpEvent.tags,
83         "FILE_SIZE", dumpEvent.fileSize,
84         "SYS_MEM_TOTAL", dumpEvent.sysMemTotal,
85         "SYS_MEM_FREE", dumpEvent.sysMemFree,
86         "SYS_MEM_AVAIL", dumpEvent.sysMemAvail,
87         "SYS_CPU", dumpEvent.sysCpu,
88         "TRACE_MODE", dumpEvent.traceMode);
89     if (ret != 0) {
90         HIVIEW_LOGE("HiSysEventWrite failed, ret is %{public}d", ret);
91     }
92 }
93 }
94 
DumpTrace(DumpEvent & dumpEvent,TraceRetInfo & traceRetInfo) const95 TraceRet TraceStrategy::DumpTrace(DumpEvent &dumpEvent, TraceRetInfo &traceRetInfo) const
96 {
97     InitDumpEvent(dumpEvent, caller_, maxDuration_, happenTime_);
98     uint32_t maxDuration = (maxDuration_ == FULL_TRACE_DURATION) ? 0 : maxDuration_;
99 #ifndef TRACE_STRATEGY_UNITTEST
100     TraceRet ret = TraceStateMachine::GetInstance().DumpTrace(scenario_, maxDuration, happenTime_, traceRetInfo);
101 #else
102     TraceRet ret(traceRetInfo.errorCode);
103 #endif
104     if (!ret.IsSuccess()) {
105         HIVIEW_LOGW("scenario_:%{public}d, stateError:%{public}d, codeError:%{public}d", static_cast<int>(scenario_),
106             static_cast<int>(ret.stateError_), ret.codeError_);
107     }
108     UpdateDumpEvent(dumpEvent, ret, traceRetInfo);
109     return ret;
110 }
111 
DoDump(std::vector<std::string> & outputFiles,TraceRetInfo & traceRetInfo)112 TraceRet TraceDevStrategy::DoDump(std::vector<std::string> &outputFiles, TraceRetInfo &traceRetInfo)
113 {
114     DumpEvent dumpEvent;
115     TraceRet ret = DumpTrace(dumpEvent, traceRetInfo);
116     if (!ret.IsSuccess()) {
117         WriteDumpTraceHisysevent(dumpEvent);
118         return ret;
119     }
120     if (traceRetInfo.outputFiles.empty()) {
121         HIVIEW_LOGE("TraceDevStrategy outputFiles empty.");
122         return TraceRet(TraceStateCode::FAIL);
123     }
124     const int64_t traceSize = traceRetInfo.fileSize;
125     if (traceSize <= static_cast<int64_t>(INT32_MAX) * BYTE_UNIT * BYTE_UNIT) {
126         dumpEvent.fileSize = traceSize / BYTE_UNIT / BYTE_UNIT;
127     }
128     if (traceHandler_ == nullptr) {
129         outputFiles = traceRetInfo.outputFiles;
130         return ret;
131     }
132     outputFiles = traceHandler_->HandleTrace(traceRetInfo.outputFiles);
133     if (zipHandler_ == nullptr) {
134         WriteDumpTraceHisysevent(dumpEvent);
135         return ret;
136     }
137     int64_t traceRemainingSize = traceFlowController_->GetRemainingTraceSize();
138     if (traceRemainingSize <= traceSize) {
139         dumpEvent.errorCode = TransFlowToUcError(TraceFlowCode::TRACE_UPLOAD_DENY);
140         WriteDumpTraceHisysevent(dumpEvent);
141         HIVIEW_LOGI("over flow, trace generate in special dir, can not upload.");
142         return TraceRet(TraceFlowCode::TRACE_UPLOAD_DENY);
143     }
144     outputFiles = zipHandler_->HandleTrace(traceRetInfo.outputFiles);
145     WriteDumpTraceHisysevent(dumpEvent);
146     traceFlowController_->StoreDb(traceSize);
147     return ret;
148 }
149 
DoDump(std::vector<std::string> & outputFiles,TraceRetInfo & traceRetInfo)150 TraceRet TraceFlowControlStrategy::DoDump(std::vector<std::string> &outputFiles, TraceRetInfo &traceRetInfo)
151 {
152     if (traceHandler_ == nullptr) {
153         HIVIEW_LOGE("traceHandler is null");
154         return TraceRet(TraceStateCode::FAIL);
155     }
156     if (traceFlowController_->IsOverLimit()) {
157         HIVIEW_LOGI("trace is over flow, can not dump.");
158         return TraceRet(TraceFlowCode::TRACE_DUMP_DENY);
159     }
160     DumpEvent dumpEvent;
161     TraceRet ret = DumpTrace(dumpEvent, traceRetInfo);
162     if (!ret.IsSuccess()) {
163         WriteDumpTraceHisysevent(dumpEvent);
164         return ret;
165     }
166     if (traceRetInfo.outputFiles.empty()) {
167         HIVIEW_LOGE("TraceFlowControlStrategy outputFiles empty.");
168         return TraceRet(TraceStateCode::FAIL);
169     }
170     const int64_t traceSize = traceRetInfo.fileSize;
171     if (traceSize <= static_cast<int64_t>(INT32_MAX) * BYTE_UNIT * BYTE_UNIT) {
172         dumpEvent.fileSize = traceSize / BYTE_UNIT / BYTE_UNIT;
173     }
174     if (traceSize > traceFlowController_->GetRemainingTraceSize()) {
175         dumpEvent.errorCode = TransFlowToUcError(TraceFlowCode::TRACE_UPLOAD_DENY);
176         WriteDumpTraceHisysevent(dumpEvent);
177         traceFlowController_->DecreaseDynamicThreshold();
178         HIVIEW_LOGI("trace is over flow, can not upload.");
179         return TraceRet(TraceFlowCode::TRACE_UPLOAD_DENY);
180     }
181     outputFiles = traceHandler_->HandleTrace(traceRetInfo.outputFiles);
182     WriteDumpTraceHisysevent(dumpEvent);
183     traceFlowController_->StoreDb(traceSize);
184     return ret;
185 }
186 
DoDump(std::vector<std::string> & outputFiles,TraceRetInfo & traceRetInfo)187 TraceRet TelemetryStrategy::DoDump(std::vector<std::string> &outputFiles, TraceRetInfo &traceRetInfo)
188 {
189     if (traceHandler_ == nullptr) {
190         HIVIEW_LOGW("traceHandler is null");
191         return TraceRet(TraceStateCode::FAIL);
192     }
193 #ifndef TRACE_STRATEGY_UNITTEST
194     if (auto ret = TraceStateMachine::GetInstance().DumpTraceWithFilter(maxDuration_, happenTime_, traceRetInfo);
195 #else
196     if (TraceRet ret(traceRetInfo.errorCode);
197 #endif
198         !ret.IsSuccess()) {
199         HIVIEW_LOGE("fail stateError:%{public}d codeError:%{public}d", static_cast<int>(ret.GetStateError()),
200             static_cast<int>(ret.GetCodeError()));
201         return ret;
202     }
203     if (auto flowRet = traceFlowController_->NeedTelemetryDump(caller_); flowRet != TelemetryRet::SUCCESS) {
204         HIVIEW_LOGI("trace is over flow, can not dump.");
205         return TraceRet(TraceFlowCode::TRACE_DUMP_DENY);
206     }
207     if (traceRetInfo.outputFiles.empty()) {
208         HIVIEW_LOGW("TraceFlowControlStrategy outputFiles empty.");
209         return TraceRet(TraceStateCode::FAIL);
210     }
211     outputFiles = traceHandler_->HandleTrace(traceRetInfo.outputFiles,
212         [strategy = shared_from_this()](int64_t zipTraffic) {
213         strategy->traceFlowController_->TelemetryStore(strategy->caller_, zipTraffic);
214         HIVIEW_LOGI("storage zipTraffic:%{public}" PRId64 "", zipTraffic);
215     });
216     return {};
217 }
218 
DoDump(std::vector<std::string> & outputFiles,TraceRetInfo & traceRetInfo)219 TraceRet TraceAsyncStrategy::DoDump(std::vector<std::string> &outputFiles, TraceRetInfo &traceRetInfo)
220 {
221     DumpEvent dumpEvent;
222     TraceRet ret = DumpTrace(dumpEvent, traceRetInfo);
223     HIVIEW_LOGI("caller:%{public}s trace size:%{public}" PRId64 "", caller_.c_str(), traceRetInfo.fileSize);
224     if (!ret.IsSuccess()) {
225         WriteDumpTraceHisysevent(dumpEvent);
226         return ret;
227     }
228     if (traceRetInfo.outputFiles.empty()) {
229         HIVIEW_LOGE("TraceAsyncStrategy outputFiles empty.");
230         return TraceRet(TraceStateCode::FAIL);
231     }
232     const int64_t traceSize = traceRetInfo.fileSize;
233     if (traceSize <= static_cast<int64_t>(INT32_MAX) * BYTE_UNIT * BYTE_UNIT) {
234         dumpEvent.fileSize = traceSize / BYTE_UNIT / BYTE_UNIT;
235     }
236     if (traceHandler_ == nullptr) {
237         outputFiles = traceRetInfo.outputFiles;
238         return ret;
239     }
240     SetResultCopyFiles(outputFiles, traceRetInfo.outputFiles);
241     if (zipHandler_ == nullptr) {
242         WriteDumpTraceHisysevent(dumpEvent);
243         return ret;
244     }
245     if (traceRetInfo.isOverflowControl) {
246         dumpEvent.errorCode = TransFlowToUcError(TraceFlowCode::TRACE_UPLOAD_DENY);
247         WriteDumpTraceHisysevent(dumpEvent);
248         HIVIEW_LOGI("over flow, trace generate in special dir, can not upload.");
249         return TraceRet(TraceFlowCode::TRACE_UPLOAD_DENY);
250     }
251     SetResultZipFiles(outputFiles, traceRetInfo.outputFiles);
252     WriteDumpTraceHisysevent(dumpEvent);
253     traceFlowController_->StoreDb(traceSize);
254     return ret;
255 }
256 
DumpTrace(DumpEvent & dumpEvent,TraceRetInfo & traceRetInfo) const257 TraceRet TraceAsyncStrategy::DumpTrace(DumpEvent &dumpEvent, TraceRetInfo &traceRetInfo) const
258 {
259     InitDumpEvent(dumpEvent, caller_, maxDuration_, happenTime_);
260     DumpTraceArgs args = {scenario_, maxDuration_, happenTime_};
261     int64_t traceRemainingSize = traceFlowController_->GetRemainingTraceSize();
262 #ifndef TRACE_STRATEGY_UNITTEST
263     TraceRet ret = TraceStateMachine::GetInstance().DumpTraceAsync(args, traceRemainingSize, traceRetInfo,
264 #else
265     TraceRet ret = MockTraceStateMachine::GetInstance().DumpTraceAsync(args, traceRemainingSize, traceRetInfo,
266 #endif
267         [strategy = shared_from_this()](TraceRetInfo asyncTraceRetInfo) {
268             if (asyncTraceRetInfo.errorCode != TraceErrorCode::SUCCESS || asyncTraceRetInfo.outputFiles.empty()) {
269                 HIVIEW_LOGW("caller %{public}s callback not implement or trace file empty", strategy->caller_.c_str());
270                 return;
271             }
272             if (strategy->traceHandler_ != nullptr) {
273                 strategy->traceHandler_->HandleTrace(asyncTraceRetInfo.outputFiles);
274             }
275             if (strategy->zipHandler_ != nullptr && !asyncTraceRetInfo.isOverflowControl) {
276                 strategy->zipHandler_ ->HandleTrace(asyncTraceRetInfo.outputFiles);
277             }
278     });
279     UpdateDumpEvent(dumpEvent, ret, traceRetInfo);
280     return ret;
281 }
282 
DoDump(std::vector<std::string> & outputFiles,TraceRetInfo & traceRetInfo)283 TraceRet TraceAppStrategy::DoDump(std::vector<std::string> &outputFiles, TraceRetInfo &traceRetInfo)
284 {
285 #ifndef TRACE_STRATEGY_UNITTEST
286     auto appPid = TraceStateMachine::GetInstance().GetCurrentAppPid();
287 #else
288     auto appPid = MockTraceStateMachine::GetInstance().GetCurrentAppPid();
289 #endif
290     if (appPid != appCallerEvent_->pid_) {
291         HIVIEW_LOGW("pid check fail, maybe is app state closed, current:%{public}d, pid:%{public}d", appPid,
292             appCallerEvent_->pid_);
293         return TraceRet(TraceStateCode::FAIL);
294     }
295     if (traceFlowController_->HasCallOnceToday(appCallerEvent_->uid_, appCallerEvent_->happenTime_)) {
296         HIVIEW_LOGE("already capture trace uid=%{public}d pid==%{public}d", appCallerEvent_->uid_,
297             appCallerEvent_->pid_);
298         return TraceRet(TraceFlowCode::TRACE_HAS_CAPTURED_TRACE);
299     }
300 #ifndef TRACE_STRATEGY_UNITTEST
301     TraceRet ret = TraceStateMachine::GetInstance().DumpTrace(scenario_, 0, 0, traceRetInfo);
302     appCallerEvent_->taskBeginTime_ = static_cast<int64_t>(TraceStateMachine::GetInstance().GetTaskBeginTime());
303 #else
304     TraceRet ret(traceRetInfo.errorCode);
305 #endif
306     appCallerEvent_->taskEndTime_ = static_cast<int64_t>(TimeUtil::GetMilliseconds());
307     if (ret.IsSuccess()) {
308         if (traceRetInfo.outputFiles.empty() || traceRetInfo.outputFiles[0].empty()) {
309             HIVIEW_LOGE("TraceAppStrategy dump file empty");
310             return TraceRet(TraceStateCode::FAIL);
311         }
312         if (traceHandler_ != nullptr) {
313             outputFiles = traceHandler_->HandleTrace(traceRetInfo.outputFiles, {}, appCallerEvent_);
314         }
315         traceFlowController_->RecordCaller(appCallerEvent_);
316     }
317     ShareAppEvent(appCallerEvent_);
318     CleanOldAppTrace();
319     ReportMainThreadJankForTrace(appCallerEvent_);
320     return ret;
321 }
322 
ReportMainThreadJankForTrace(std::shared_ptr<AppCallerEvent> appCallerEvent)323 void TraceAppStrategy::ReportMainThreadJankForTrace(std::shared_ptr<AppCallerEvent> appCallerEvent)
324 {
325     HiSysEventWrite(HiSysEvent::Domain::FRAMEWORK, UCollectUtil::MAIN_THREAD_JANK, HiSysEvent::EventType::FAULT,
326         UCollectUtil::SYS_EVENT_PARAM_BUNDLE_NAME, appCallerEvent->bundleName_,
327         UCollectUtil::SYS_EVENT_PARAM_BUNDLE_VERSION, appCallerEvent->bundleVersion_,
328         UCollectUtil::SYS_EVENT_PARAM_BEGIN_TIME, appCallerEvent->beginTime_,
329         UCollectUtil::SYS_EVENT_PARAM_END_TIME, appCallerEvent->endTime_,
330         UCollectUtil::SYS_EVENT_PARAM_THREAD_NAME, appCallerEvent->threadName_,
331         UCollectUtil::SYS_EVENT_PARAM_FOREGROUND, appCallerEvent->foreground_,
332         UCollectUtil::SYS_EVENT_PARAM_LOG_TIME, appCallerEvent->taskEndTime_,
333         UCollectUtil::SYS_EVENT_PARAM_JANK_LEVEL, 1); // 1: over 450ms
334 }
335 
CleanOldAppTrace()336 void TraceAppStrategy::CleanOldAppTrace()
337 {
338     uint64_t timeNow = TimeUtil::GetMilliseconds() / TimeUtil::SEC_TO_MILLISEC;
339     uint32_t secondsOfThreeDays = 3 * TimeUtil::SECONDS_PER_DAY; // 3 : clean data three days ago
340     if (timeNow < secondsOfThreeDays) {
341         HIVIEW_LOGW("time is invalid");
342         return;
343     }
344     uint64_t timeThreeDaysAgo = timeNow - secondsOfThreeDays;
345     std::string dateThreeDaysAgo = TimeUtil::TimestampFormatToDate(timeThreeDaysAgo, "%Y%m%d");
346     int32_t dateNum = 0;
347     auto result = std::from_chars(dateThreeDaysAgo.c_str(), dateThreeDaysAgo.c_str() + dateThreeDaysAgo.size(),
348         dateNum);
349     if (result.ec != std::errc()) {
350         HIVIEW_LOGW("convert error, dateStr: %{public}s", dateThreeDaysAgo.c_str());
351         return;
352     }
353     traceFlowController_->CleanOldAppTrace(dateNum);
354 }
355 
ShareAppEvent(std::shared_ptr<AppCallerEvent> appCallerEvent)356 void TraceAppStrategy::ShareAppEvent(std::shared_ptr<AppCallerEvent> appCallerEvent)
357 {
358     Json::Value eventJson;
359     eventJson[UCollectUtil::APP_EVENT_PARAM_UID] = appCallerEvent->uid_;
360     eventJson[UCollectUtil::APP_EVENT_PARAM_PID] = appCallerEvent->pid_;
361     eventJson[UCollectUtil::APP_EVENT_PARAM_TIME] = appCallerEvent->happenTime_;
362     eventJson[UCollectUtil::APP_EVENT_PARAM_BUNDLE_NAME] = appCallerEvent->bundleName_;
363     eventJson[UCollectUtil::APP_EVENT_PARAM_BUNDLE_VERSION] = appCallerEvent->bundleVersion_;
364     eventJson[UCollectUtil::APP_EVENT_PARAM_BEGIN_TIME] = appCallerEvent->beginTime_;
365     eventJson[UCollectUtil::APP_EVENT_PARAM_END_TIME] = appCallerEvent->endTime_;
366     eventJson[UCollectUtil::APP_EVENT_PARAM_ISBUSINESSJANK] = appCallerEvent->isBusinessJank_;
367     Json::Value externalLog;
368     externalLog.append(appCallerEvent->externalLog_);
369     eventJson[UCollectUtil::APP_EVENT_PARAM_EXTERNAL_LOG] = externalLog;
370     std::string param = Json::FastWriter().write(eventJson);
371     HIVIEW_LOGI("send for uid=%{public}d pid=%{public}d", appCallerEvent->uid_, appCallerEvent->pid_);
372     EventPublish::GetInstance().PushEvent(appCallerEvent->uid_, UCollectUtil::MAIN_THREAD_JANK,
373         HiSysEvent::EventType::FAULT, param);
374 }
375 }
376