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 "uc_telemetry_listener.h"
16
17 #include "cjson_util.h"
18 #include "hiview_logger.h"
19 #include "time_util.h"
20 #include "hisysevent.h"
21 #include "uc_telemetry_callback.h"
22
23 namespace OHOS::HiviewDFX {
24 DEFINE_LOG_TAG("HiView-UnifiedCollector");
25 namespace {
26 const int64_t DURATION_DEFAULT = 3600 * SECONDS_TO_MS; // ms
27 const int64_t MAX_DURATION = 7 * 24 * 3600 * SECONDS_TO_MS; // ms
28 const uint32_t BT_M_UNIT = 1024 * 1024;
29 const int64_t MAX_BUFFER_SIZE = 500 * 1024; // 500M
30 constexpr char TELEMETRY_DOMAIN[] = "TELEMETRY";
31 constexpr char TAGS[] = "tags";
32 constexpr char BUFFER_SIZE[] = "bufferSize";
33
34 constexpr char SWITCH_ON[] = "on";
35 constexpr char SWITCH_OFF[] = "off";
36 constexpr char POLICY_POWER[] = "power";
37 constexpr char POLICY_MANUAL[] = "manual";
38
39 // Default quota of flow control
40 const int64_t DEFAULT_XPERF_SIZE = 20 * 1024 * 1024;
41 const int64_t DEFAULT_XPOWER_SIZE = 20 * 1024 * 1024;
42 const int64_t DEFAULT_RELIABILITY_SIZE = 20 * 1024 * 1024;
43 const int64_t DEFAULT_TOTAL_SIZE = 50 * 1024 * 1024;
44 const int64_t MAX_TOTAL_SIZE = 1024; // 1G
45
46 constexpr char KEY_ID[] = "telemetryId";
47 constexpr char KEY_FILTER_NAME[] = "appFilterName";
48 constexpr char KEY_SA_NAMES[] = "saNames";
49 constexpr char KEY_SWITCH_STATUS[] = "telemetryStatus";
50 constexpr char KEY_TRACE_POLICY[] = "tracePolicy";
51 constexpr char KEY_TRACE_TAG[] = "traceArgs";
52 constexpr char KEY_TOTAL_QUOTA[] = "traceQuota";
53 constexpr char KEY_OPEN_TIME[] = "traceOpenTime";
54 constexpr char KEY_DURATION[] = "traceDuration";
55 constexpr char KEY_XPERF_QUOTA[] = "xperfTraceQuota";
56 constexpr char KEY_XPOWER_QUOTA[] = "xpowerTraceQuota";
57 constexpr char KEY_RELIABILITY_QUOTA[] = "reliabilityTraceQuota";
58 constexpr char TOTAL[] = "Total";
59
60 const std::unordered_set<std::string> TRACE_TAG_FILTER_LIST {
61 "sched", "freq", "disk", "sync", "binder", "mmc", "membus", "load", "pagecache", "workq", "net", "dsched",
62 "graphic", "multimodalinput", "dinput", "ark", "ace", "window", "zaudio", "daudio", "zmedia", "dcamera",
63 "zcamera", "dhfwk", "app", "ability", "power", "samgr", "nweb"
64 };
65
66 const std::unordered_set<std::string> TRACE_SA_FILTER_LIST {
67 "render_service", "foundation"
68 };
69
ParseAndFilterTraceArgs(const std::unordered_set<std::string> & filterList,cJSON * root,const std::string & key)70 std::vector<std::string> ParseAndFilterTraceArgs(const std::unordered_set<std::string> &filterList,
71 cJSON* root, const std::string &key)
72 {
73 if (!cJSON_IsObject(root)) {
74 HIVIEW_LOGE("trace jsonArgs parse error");
75 return {};
76 }
77 std::vector<std::string> traceArgs;
78 CJsonUtil::GetStringArray(root, key, traceArgs);
79 auto new_end = std::remove_if(traceArgs.begin(), traceArgs.end(), [&filterList](const std::string& tag) {
80 return filterList.find(tag) == filterList.end();
81 });
82 traceArgs.erase(new_end, traceArgs.end());
83 return traceArgs;
84 }
85 }
86
OnUnorderedEvent(const Event & msg)87 void TelemetryListener::OnUnorderedEvent(const Event &msg)
88 {
89 bool isCloseMsg = false;
90 TelemetryParams params;
91 std::string errorMsg = CheckValidParam(msg, params, isCloseMsg);
92 HIVIEW_LOGI("isClose:%{public}d", isCloseMsg);
93 if (!errorMsg.empty()) {
94 return WriteErrorEvent(errorMsg, params);
95 }
96 if (isCloseMsg) {
97 HandleStop();
98 return;
99 }
100 params.appFilterName = msg.GetValue(KEY_FILTER_NAME);
101 params.traceDuration = msg.GetInt64Value(KEY_DURATION) * SECONDS_TO_MS;
102 if (params.traceDuration <= 0) {
103 params.traceDuration = DURATION_DEFAULT;
104 } else if (params.traceDuration > MAX_DURATION) {
105 params.traceDuration = MAX_DURATION;
106 }
107 GetSaNames(msg, params);
108
109 bool isTimeOut = false;
110 if (!InitTelemetryDbData(msg, isTimeOut, params)) {
111 return WriteErrorEvent("init telemetry time table fail", params);
112 }
113 if (isTimeOut) {
114 HIVIEW_LOGE("%{public}s", "trace already time out");
115 return;
116 }
117 auto delaySeconds = params.beginTime - TimeUtil::GetSeconds();
118 if (delaySeconds <= 0) {
119 HandleStart(params);
120 } else {
121 if (taskQueue_ == nullptr) {
122 taskQueue_ = std::make_unique<ffrt::queue>("telemetry_queue");
123 }
124 startTaskHandle_ = taskQueue_->submit_h([this, params] { this->HandleStart(params); },
125 ffrt::task_attr().delay(delaySeconds * SECONDS_TO_MS * MS_TO_US));
126 }
127 }
128
CheckValidParam(const Event & msg,TelemetryParams & params,bool & isCloseMsg)129 std::string TelemetryListener::CheckValidParam(const Event &msg, TelemetryParams ¶ms, bool &isCloseMsg)
130 {
131 std::string errorMsg;
132 if (!CheckTelemetryId(msg, params, errorMsg)) {
133 return errorMsg;
134 }
135 if (!CheckTraceTags(msg, params, errorMsg)) {
136 return errorMsg;
137 }
138 if (!CheckTracePolicy(msg, params, errorMsg)) {
139 return errorMsg;
140 }
141 if (!CheckSwitchValid(msg, isCloseMsg, errorMsg)) {
142 return errorMsg;
143 }
144 if (!CheckBeginTime(msg, params, errorMsg)) {
145 return errorMsg;
146 }
147 return errorMsg;
148 }
149
InitTelemetryDbData(const Event & msg,bool & isTimeOut,const TelemetryParams & params)150 bool TelemetryListener::InitTelemetryDbData(const Event &msg, bool &isTimeOut, const TelemetryParams ¶ms)
151 {
152 std::map<std::string, int64_t> flowControlQuotas {
153 {CallerName::XPERF, DEFAULT_XPERF_SIZE },
154 {CallerName::XPOWER, DEFAULT_XPOWER_SIZE},
155 {CallerName::RELIABILITY, DEFAULT_RELIABILITY_SIZE},
156 {TOTAL, DEFAULT_TOTAL_SIZE}
157 };
158 auto xperfTraceQuota = msg.GetInt64Value(KEY_XPERF_QUOTA);
159 if (xperfTraceQuota > 0) {
160 flowControlQuotas[CallerName::XPERF] = xperfTraceQuota * BT_M_UNIT;
161 }
162 auto xpowerTraceQuota = msg.GetInt64Value(KEY_XPOWER_QUOTA);
163 if (xpowerTraceQuota > 0) {
164 flowControlQuotas[CallerName::XPOWER] = xpowerTraceQuota * BT_M_UNIT;
165 }
166 auto reliabilityTraceQuota = msg.GetInt64Value(KEY_RELIABILITY_QUOTA);
167 if (reliabilityTraceQuota > 0) {
168 flowControlQuotas[CallerName::RELIABILITY] = reliabilityTraceQuota * BT_M_UNIT;
169 }
170 auto totalTraceQuota = msg.GetInt64Value(KEY_TOTAL_QUOTA);
171 if (totalTraceQuota > 0 && totalTraceQuota <= MAX_TOTAL_SIZE) {
172 flowControlQuotas[TOTAL] = totalTraceQuota * BT_M_UNIT;
173 } else if (totalTraceQuota > MAX_TOTAL_SIZE) {
174 flowControlQuotas[TOTAL] = MAX_TOTAL_SIZE * BT_M_UNIT;
175 } else {
176 HIVIEW_LOGI("default total quota size");
177 }
178
179 int64_t running_time = 0;
180 auto ret = TraceFlowController(BusinessName::TELEMETRY).InitTelemetryData(params.telemetryId, running_time,
181 flowControlQuotas);
182 if (ret == TelemetryRet::EXIT) {
183 return false;
184 }
185 isTimeOut = running_time >= params.traceDuration;
186 return true;
187 }
188
HandleStart(const TelemetryParams & params)189 void TelemetryListener::HandleStart(const TelemetryParams ¶ms)
190 {
191 auto ret = TraceStateMachine::GetInstance().OpenTelemetryTrace(params.traceTag, params.tracePolicy);
192 if (ret.IsSuccess()) {
193 std::shared_ptr<TelemetryCallback> callback;
194 switch (params.tracePolicy) {
195 case TelemetryPolicy::POWER:
196 callback = std::make_shared<PowerCallback>(params);
197 break;
198 case TelemetryPolicy::MANUAL:
199 callback = std::make_shared<ManualCallback>(params);
200 break;
201 default:
202 callback = std::make_shared<UcTelemetryCallback>(params);
203 break;
204 }
205 bool isSuccess = TraceStateMachine::GetInstance().RegisterTelemetryCallback(callback);
206 HIVIEW_LOGI("register callback result:%{public}d, traceDuration%{public}" PRId64 "", isSuccess,
207 params.traceDuration);
208 } else {
209 WriteErrorEvent("trace state error", params);
210 }
211 }
212
HandleStop()213 void TelemetryListener::HandleStop()
214 {
215 TraceStateMachine::GetInstance().CloseTrace(TraceScenario::TRACE_TELEMETRY);
216 TraceFlowController controller(BusinessName::TELEMETRY);
217 controller.ClearTelemetryData();
218 if (taskQueue_ != nullptr && taskQueue_->cancel(startTaskHandle_) < 0) {
219 HIVIEW_LOGW("%{public}s", "telemetstartTaskHandle_ry trace already start");
220 }
221 }
222
WriteErrorEvent(const std::string & error,const TelemetryParams & params)223 void TelemetryListener::WriteErrorEvent(const std::string &error, const TelemetryParams ¶ms)
224 {
225 HIVIEW_LOGE("%{public}s", error.c_str());
226 HiSysEventWrite(TELEMETRY_DOMAIN, "TASK_INFO", HiSysEvent::EventType::STATISTIC,
227 "ID", params.telemetryId,
228 "STAGE", "TRACE_BEGIN",
229 "ERROR", error);
230 }
231
ProcessTraceTag(std::string & traceTag)232 bool TelemetryListener::ProcessTraceTag(std::string &traceTag)
233 {
234 cJSON* root = cJSON_Parse(traceTag.c_str());
235 if (root == nullptr) {
236 return false;
237 }
238 auto tags = ParseAndFilterTraceArgs(TRACE_TAG_FILTER_LIST, root, TAGS);
239 if (tags.empty()) {
240 cJSON_Delete(root);
241 return false;
242 }
243 auto bufferSize = CJsonUtil::GetIntValue(root, BUFFER_SIZE);
244 cJSON_Delete(root);
245 if (bufferSize <= 0) {
246 HIVIEW_LOGE("jsonArgs parse trace bufferSize error");
247 return false;
248 }
249 if (bufferSize > MAX_BUFFER_SIZE) {
250 bufferSize = MAX_BUFFER_SIZE;
251 }
252 bool isFirst = true;
253 std::string result("tags:");
254 for (const auto &tag: tags) {
255 if (!isFirst) {
256 result.append(", ").append(tag);
257 continue;
258 }
259 result.append(tag);
260 isFirst = false;
261 }
262 result.append(" bufferSize:").append(std::to_string(bufferSize));
263 traceTag = std::move(result);
264 return true;
265 }
266
CheckTelemetryId(const Event & msg,TelemetryParams & params,std::string & errorMsg)267 bool TelemetryListener::CheckTelemetryId(const Event &msg, TelemetryParams ¶ms, std::string &errorMsg)
268 {
269 std::string telemetryId = msg.GetValue(KEY_ID);
270 if (telemetryId.empty()) {
271 errorMsg.append("telemetryId get empty");
272 return false;
273 }
274 params.telemetryId = telemetryId;
275 return true;
276 }
277
GetSaNames(const Event & msg,TelemetryParams & params)278 void TelemetryListener::GetSaNames(const Event &msg, TelemetryParams ¶ms)
279 {
280 std::string saJsonNames = msg.GetValue(KEY_SA_NAMES);
281 if (!saJsonNames.empty()) {
282 cJSON* root = cJSON_Parse(saJsonNames.c_str());
283 if (root == nullptr) {
284 return;
285 }
286 auto saNames = ParseAndFilterTraceArgs(TRACE_SA_FILTER_LIST, root, KEY_SA_NAMES);
287 for (const auto &saName : saNames) {
288 auto param = "startup.service.ctl." + saName + ".pid";
289 params.saParams.emplace_back(param);
290 }
291 cJSON_Delete(root);
292 }
293 }
294
CheckTraceTags(const Event & msg,TelemetryParams & params,std::string & errorMsg)295 bool TelemetryListener::CheckTraceTags(const Event &msg, TelemetryParams ¶ms, std::string &errorMsg)
296 {
297 auto traceTag = msg.GetValue(KEY_TRACE_TAG);
298 if (!traceTag.empty() && !ProcessTraceTag(traceTag)) {
299 errorMsg.append("process trace tag fail");
300 return false;
301 }
302 params.traceTag = traceTag;
303 return true;
304 }
305
CheckTracePolicy(const Event & msg,TelemetryParams & params,std::string & errorMsg)306 bool TelemetryListener::CheckTracePolicy(const Event &msg, TelemetryParams ¶ms, std::string &errorMsg)
307 {
308 auto tracePolicy = msg.GetValue(KEY_TRACE_POLICY);
309 if (tracePolicy.empty()) {
310 params.tracePolicy = TelemetryPolicy::DEFAULT;
311 return true;
312 }
313 if (tracePolicy == POLICY_POWER) {
314 params.tracePolicy = TelemetryPolicy::POWER;
315 } else if (tracePolicy == POLICY_MANUAL) {
316 params.tracePolicy = TelemetryPolicy::MANUAL;
317 } else {
318 errorMsg.append("trace policy get empty");
319 return false;
320 }
321 return true;
322 }
323
CheckSwitchValid(const Event & msg,bool & isCloseMsg,std::string & errorMsg)324 bool TelemetryListener::CheckSwitchValid(const Event &msg, bool &isCloseMsg, std::string &errorMsg)
325 {
326 auto switchStatus = msg.GetValue(KEY_SWITCH_STATUS);
327 if (switchStatus.empty()) {
328 errorMsg.append("switchStatus get empty");
329 return false;
330 }
331 if (switchStatus == SWITCH_OFF) {
332 isCloseMsg = true;
333 return false;
334 }
335 if (switchStatus != SWITCH_ON) {
336 errorMsg.append("switchStatus param get error");
337 return false;
338 }
339 return true;
340 }
341
CheckBeginTime(const Event & msg,TelemetryParams & params,std::string & errorMsg)342 bool TelemetryListener::CheckBeginTime(const Event &msg, TelemetryParams ¶ms, std::string &errorMsg)
343 {
344 // Get begin time of telemetry trace unit seconds
345 int64_t beginTime = msg.GetInt64Value(KEY_OPEN_TIME);
346 if (beginTime < 0) {
347 errorMsg.append("begin time get failed");
348 return false;
349 }
350 params.beginTime = beginTime;
351 return true;
352 }
353 }
354