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