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