1 /*
2 * Copyright (C) 2023-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 "unified_collector.h"
16
17 #include <ctime>
18 #include <memory>
19 #include <sys/file.h>
20
21 #include "collect_event.h"
22 #include "ffrt.h"
23 #include "file_util.h"
24 #include "hiview_logger.h"
25 #include "io_collector.h"
26 #include "parameter_ex.h"
27 #ifdef HAS_HIPERF
28 #include "perf_collect_config.h"
29 #endif
30 #include "plugin_factory.h"
31 #include "process_status.h"
32 #include "sys_event.h"
33 #include "string_util.h"
34 #include "uc_observer_mgr.h"
35 #include "unified_collection_stat.h"
36 #ifdef UNIFIED_COLLECTOR_TRACE_ENABLE
37 #include "event_publish.h"
38 #include "hisysevent.h"
39 #include "json/json.h"
40 #include "trace_state_machine.h"
41 #include "uc_telemetry_listener.h"
42 #include "trace_collector.h"
43 #endif
44
45 namespace OHOS {
46 namespace HiviewDFX {
47 REGISTER(UnifiedCollector);
48 DEFINE_LOG_TAG("HiView-UnifiedCollector");
49 using namespace OHOS::HiviewDFX::UCollectUtil;
50 using namespace std::literals::chrono_literals;
51 namespace {
52 constexpr char HIPERF_LOG_PATH[] = "/data/log/hiperf";
53 constexpr char COLLECTION_IO_PATH[] = "/data/log/hiview/unified_collection/io/";
54 constexpr char HIVIEW_UCOLLECTION_STATE_TRUE[] = "true";
55 constexpr char HIVIEW_UCOLLECTION_STATE_FALSE[] = "false";
56 #ifdef UNIFIED_COLLECTOR_TRACE_ENABLE
57 constexpr char DEVELOP_TRACE_RECORDER_FALSE[] = "false";
58 constexpr char KEY_FREEZE_DETECTOR_STATE[] = "persist.hiview.freeze_detector";
59 constexpr char OTHER[] = "Other";
60 using namespace OHOS::HiviewDFX::Hitrace;
61 constexpr int32_t DURATION_TRACE = 10; // unit second
62 constexpr char UNIFIED_SPECIAL_PATH[] = "/data/log/hiview/unified_collection/trace/special/";
63 constexpr char UNIFIED_TELEMETRY_PATH[] = "/data/log/hiview/unified_collection/trace/telemetry/";
64 constexpr char UNIFIED_SHARE_TEMP_PATH[] = "/data/log/hiview/unified_collection/trace/share/temp/";
65
CreateTracePathInner(const std::string & filePath)66 void CreateTracePathInner(const std::string &filePath)
67 {
68 if (FileUtil::FileExists(filePath)) {
69 return;
70 }
71 if (!FileUtil::CreateMultiDirectory(filePath)) {
72 HIVIEW_LOGE("failed to create multidirectory %{public}s.", filePath.c_str());
73 }
74 }
75
CreateTracePath()76 void CreateTracePath()
77 {
78 CreateTracePathInner(UNIFIED_SHARE_TEMP_PATH);
79 CreateTracePathInner(UNIFIED_SPECIAL_PATH);
80 CreateTracePathInner(UNIFIED_TELEMETRY_PATH);
81 }
82 #endif
83 }
84
OnLoad()85 void UnifiedCollector::OnLoad()
86 {
87 HIVIEW_LOGI("start to load UnifiedCollector plugin");
88 Init();
89 }
90
OnUnload()91 void UnifiedCollector::OnUnload()
92 {
93 HIVIEW_LOGI("start to unload UnifiedCollector plugin");
94 UcObserverManager::GetInstance().UnregisterObservers();
95 }
96
97 #ifdef UNIFIED_COLLECTOR_TRACE_ENABLE
OnFreezeDetectorParamChanged(const char * key,const char * value,void * context)98 void UnifiedCollector::OnFreezeDetectorParamChanged(const char* key, const char* value, void* context)
99 {
100 if (key == nullptr || value == nullptr) {
101 HIVIEW_LOGW("key or value is null");
102 return;
103 }
104 if (strncmp(key, KEY_FREEZE_DETECTOR_STATE, strlen(KEY_FREEZE_DETECTOR_STATE)) != 0) {
105 HIVIEW_LOGW("key is not wanted, key: %{public}s", key);
106 return;
107 }
108 HIVIEW_LOGI("freeze detector param changed, value: %{public}s", value);
109 if (strncmp(value, "true", strlen("true")) == 0) {
110 TraceStateMachine::GetInstance().SetTraceSwitchFreezeOn();
111 } else {
112 TraceStateMachine::GetInstance().SetTraceSwitchFreezeOff();
113 }
114 }
115
OnSwitchRecordTraceStateChanged(const char * key,const char * value,void * context)116 void UnifiedCollector::OnSwitchRecordTraceStateChanged(const char* key, const char* value, void* context)
117 {
118 if (key == nullptr || value == nullptr) {
119 HIVIEW_LOGE("record trace switch input ptr null");
120 return;
121 }
122 if (strncmp(key, DEVELOP_HIVIEW_TRACE_RECORDER, strlen(DEVELOP_HIVIEW_TRACE_RECORDER)) != 0) {
123 HIVIEW_LOGE("record trace switch param key error");
124 return;
125 }
126 if (strncmp(value, "true", strlen("true")) == 0) {
127 TraceStateMachine::GetInstance().SetTraceSwitchDevOn();
128 } else {
129 TraceStateMachine::GetInstance().SetTraceSwitchDevOff();
130 }
131 }
132
LoadTraceSwitch()133 void UnifiedCollector::LoadTraceSwitch()
134 {
135 if (Parameter::IsBetaVersion()) {
136 TraceStateMachine::GetInstance().SetTraceVersionBeta();
137 } else {
138 int watchFreezeRet = Parameter::WatchParamChange(KEY_FREEZE_DETECTOR_STATE,
139 OnFreezeDetectorParamChanged, nullptr);
140 HIVIEW_LOGI("watchFreezeRet:%{public}d", watchFreezeRet);
141 }
142 if (Parameter::IsUCollectionSwitchOn()) {
143 TraceStateMachine::GetInstance().SetTraceSwitchUcOn();
144 }
145 if (Parameter::GetBoolean(KEY_FREEZE_DETECTOR_STATE, false)) {
146 TraceStateMachine::GetInstance().SetTraceSwitchFreezeOn();
147 }
148 if (Parameter::IsDeveloperMode()) {
149 if (Parameter::IsTraceCollectionSwitchOn()) {
150 TraceStateMachine::GetInstance().SetTraceSwitchDevOn();
151 }
152 int ret = Parameter::WatchParamChange(DEVELOP_HIVIEW_TRACE_RECORDER, OnSwitchRecordTraceStateChanged, this);
153 HIVIEW_LOGI("add ucollection trace switch param watcher ret: %{public}d", ret);
154 }
155 }
156
OnMainThreadJank(SysEvent & sysEvent)157 void UnifiedCollector::OnMainThreadJank(SysEvent& sysEvent)
158 {
159 if (sysEvent.GetEventIntValue(UCollectUtil::SYS_EVENT_PARAM_JANK_LEVEL) <
160 UCollectUtil::SYS_EVENT_JANK_LEVEL_VALUE_TRACE) {
161 // hicollie capture stack in application process, only need to share app event to application by hiview
162 Json::Value eventJson;
163 eventJson[UCollectUtil::APP_EVENT_PARAM_UID] = sysEvent.GetUid();
164 eventJson[UCollectUtil::APP_EVENT_PARAM_PID] = sysEvent.GetPid();
165 eventJson[UCollectUtil::APP_EVENT_PARAM_TIME] = sysEvent.happenTime_;
166 eventJson[UCollectUtil::APP_EVENT_PARAM_BUNDLE_NAME] = sysEvent.GetEventValue(
167 UCollectUtil::SYS_EVENT_PARAM_BUNDLE_NAME);
168 eventJson[UCollectUtil::APP_EVENT_PARAM_BUNDLE_VERSION] = sysEvent.GetEventValue(
169 UCollectUtil::SYS_EVENT_PARAM_BUNDLE_VERSION);
170 eventJson[UCollectUtil::APP_EVENT_PARAM_BEGIN_TIME] = sysEvent.GetEventIntValue(
171 UCollectUtil::SYS_EVENT_PARAM_BEGIN_TIME);
172 eventJson[UCollectUtil::APP_EVENT_PARAM_END_TIME] = sysEvent.GetEventIntValue(
173 UCollectUtil::SYS_EVENT_PARAM_END_TIME);
174 eventJson[UCollectUtil::APP_EVENT_PARAM_APP_START_JIFFIES_TIME] = sysEvent.GetEventIntValue(
175 UCollectUtil::SYS_EVENT_PARAM_APP_START_JIFFIES_TIME);
176 eventJson[UCollectUtil::APP_EVENT_PARAM_HEAVIEST_STACK] = sysEvent.GetEventValue(
177 UCollectUtil::SYS_EVENT_PARAM_HEAVIEST_STACK);
178 Json::Value externalLog;
179 externalLog.append(sysEvent.GetEventValue(UCollectUtil::SYS_EVENT_PARAM_EXTERNAL_LOG));
180 eventJson[UCollectUtil::APP_EVENT_PARAM_EXTERNAL_LOG] = externalLog;
181 std::string param = Json::FastWriter().write(eventJson);
182
183 HIVIEW_LOGI("send as stack trigger for uid=%{public}d pid=%{public}d", sysEvent.GetUid(), sysEvent.GetPid());
184 EventPublish::GetInstance().PushEvent(sysEvent.GetUid(), UCollectUtil::MAIN_THREAD_JANK,
185 HiSysEvent::EventType::FAULT, param);
186 }
187 }
188
OnEvent(std::shared_ptr<Event> & event)189 bool UnifiedCollector::OnEvent(std::shared_ptr<Event>& event)
190 {
191 if (event == nullptr || workLoop_ == nullptr) {
192 return true;
193 }
194 HIVIEW_LOGI("Receive Event %{public}s", event->GetEventName().c_str());
195 if (event->eventName_ == UCollectUtil::START_APP_TRACE) {
196 event->eventName_ = UCollectUtil::STOP_APP_TRACE;
197 DelayProcessEvent(event, DURATION_TRACE);
198 return true;
199 }
200 if (event->eventName_ == UCollectUtil::STOP_APP_TRACE) {
201 auto ret = TraceStateMachine::GetInstance().CloseTrace(TraceScenario::TRACE_DYNAMIC);
202 if (!ret.IsSuccess()) {
203 HIVIEW_LOGW("CloseTrace app trace fail");
204 }
205 return true;
206 }
207 return true;
208 }
209
210
OnEventListeningCallback(const Event & event)211 void UnifiedCollector::OnEventListeningCallback(const Event& event)
212 {
213 SysEvent& sysEvent = static_cast<SysEvent&>(const_cast<Event&>(event));
214 HIVIEW_LOGI("sysevent %{public}s", sysEvent.eventName_.c_str());
215
216 if (sysEvent.eventName_ == UCollectUtil::MAIN_THREAD_JANK) {
217 OnMainThreadJank(sysEvent);
218 return;
219 }
220 }
221
Dump(int fd,const std::vector<std::string> & cmds)222 void UnifiedCollector::Dump(int fd, const std::vector<std::string>& cmds)
223 {
224 dprintf(fd, "device beta state is %s.\n", Parameter::IsBetaVersion() ? "beta" : "is not beta");
225
226 std::string remoteLogState = Parameter::GetString(HIVIEW_UCOLLECTION_STATE, HIVIEW_UCOLLECTION_STATE_FALSE);
227 dprintf(fd, "remote log state is %s.\n", remoteLogState.c_str());
228
229 std::string traceRecorderState = Parameter::GetString(DEVELOP_HIVIEW_TRACE_RECORDER, DEVELOP_TRACE_RECORDER_FALSE);
230 dprintf(fd, "trace recorder state is %s.\n", traceRecorderState.c_str());
231
232 dprintf(fd, "develop state is %s.\n", Parameter::IsDeveloperMode() ? "true" : "false");
233
234 #ifdef HAS_HIPERF
235 std::string configPath = PerfCollectConfig::GetConfigPath();
236 for (const auto& item : PerfCollectConfig::GetPerfCount(configPath)) {
237 dprintf(fd, "perf caller : %s, concurrent counts : %d.\n",
238 PerfCollectConfig::MapPerfCallerToString(item.first).c_str(), item.second);
239 }
240 dprintf(fd, "minimum memory allowed for perf collect : %d.\n",
241 PerfCollectConfig::GetAllowMemory(configPath));
242 #endif
243 }
244
245 #ifdef HIVIEW_LOW_MEM_THRESHOLD
RunCacheMonitorLoop()246 void UnifiedCollector::RunCacheMonitorLoop()
247 {
248 if (traceCacheMonitor_ == nullptr) {
249 traceCacheMonitor_ = std::make_shared<TraceCacheMonitor>();
250 }
251 traceCacheMonitor_->RunMonitorLoop();
252 }
253
ExitCacheMonitorLoop()254 void UnifiedCollector::ExitCacheMonitorLoop()
255 {
256 if (traceCacheMonitor_ != nullptr) {
257 traceCacheMonitor_->ExitMonitorLoop();
258 }
259 }
260 #endif
261 #endif
262
Init()263 void UnifiedCollector::Init()
264 {
265 auto context = GetHiviewContext();
266 if (context == nullptr) {
267 HIVIEW_LOGE("hiview context is null");
268 return;
269 }
270 workLoop_ = context->GetSharedWorkLoop();
271 if (workLoop_ == nullptr) {
272 HIVIEW_LOGE("workLoop is null");
273 return;
274 }
275 InitWorkPath();
276 #ifdef UNIFIED_COLLECTOR_TRACE_ENABLE
277 CreateTracePath();
278 LoadTraceSwitch();
279 telemetryListener_ = std::make_shared<TelemetryListener>();
280 context->AddListenerInfo(Event::MessageType::TELEMETRY_EVENT, telemetryListener_->GetListenerName());
281 context->RegisterUnorderedEventListener(telemetryListener_);
282 TraceCollector::Create()->RecoverTmpTrace();
283 #endif
284 if (Parameter::IsBetaVersion() || Parameter::IsUCollectionSwitchOn()) {
285 RunIoCollectionTask();
286 RunUCollectionStatTask();
287 }
288 if (Parameter::IsBetaVersion() || Parameter::IsUCollectionSwitchOn() || Parameter::IsDeveloperMode()) {
289 RunCpuCollectionTask();
290 #ifdef HIVIEW_LOW_MEM_THRESHOLD
291 RunCacheMonitorLoop();
292 #endif
293 }
294 if (!Parameter::IsBetaVersion()) {
295 int watchUcollectionRet = Parameter::WatchParamChange(HIVIEW_UCOLLECTION_STATE, OnSwitchStateChanged, this);
296 HIVIEW_LOGI("watchUcollectionRet:%{public}d", watchUcollectionRet);
297 }
298 UcObserverManager::GetInstance().RegisterObservers();
299 }
300
CleanDataFiles()301 void UnifiedCollector::CleanDataFiles()
302 {
303 #ifdef UNIFIED_COLLECTOR_TRACE_ENABLE
304 std::vector<std::string> files;
305 FileUtil::GetDirFiles(UNIFIED_SPECIAL_PATH, files);
306 for (const auto& file : files) {
307 if (file.find(OTHER) != std::string::npos) {
308 FileUtil::RemoveFile(file);
309 }
310 }
311 #endif
312 FileUtil::ForceRemoveDirectory(COLLECTION_IO_PATH, false);
313 FileUtil::ForceRemoveDirectory(HIPERF_LOG_PATH, false);
314 }
315
OnSwitchStateChanged(const char * key,const char * value,void * context)316 void UnifiedCollector::OnSwitchStateChanged(const char* key, const char* value, void* context)
317 {
318 if (context == nullptr || key == nullptr || value == nullptr) {
319 HIVIEW_LOGE("input ptr null");
320 return;
321 }
322 if (strncmp(key, HIVIEW_UCOLLECTION_STATE, strlen(HIVIEW_UCOLLECTION_STATE)) != 0) {
323 HIVIEW_LOGE("param key error");
324 return;
325 }
326 HIVIEW_LOGI("ucollection switch state changed, ret: %{public}s", value);
327 auto* unifiedCollectorPtr = static_cast<UnifiedCollector*>(context);
328 if (unifiedCollectorPtr == nullptr) {
329 HIVIEW_LOGE("unifiedCollectorPtr is null");
330 return;
331 }
332 if (strncmp(value, HIVIEW_UCOLLECTION_STATE_TRUE, strlen(HIVIEW_UCOLLECTION_STATE_TRUE)) == 0) {
333 unifiedCollectorPtr->RunCpuCollectionTask();
334 unifiedCollectorPtr->RunIoCollectionTask();
335 unifiedCollectorPtr->RunUCollectionStatTask();
336 #ifdef UNIFIED_COLLECTOR_TRACE_ENABLE
337 TraceStateMachine::GetInstance().SetTraceSwitchUcOn();
338 #ifdef HIVIEW_LOW_MEM_THRESHOLD
339 unifiedCollectorPtr->RunCacheMonitorLoop();
340 #endif
341 #endif
342 } else {
343 if (!Parameter::IsDeveloperMode()) {
344 unifiedCollectorPtr->isCpuTaskRunning_ = false;
345 }
346 for (const auto &it : unifiedCollectorPtr->taskList_) {
347 unifiedCollectorPtr->workLoop_->RemoveEvent(it);
348 }
349 unifiedCollectorPtr->taskList_.clear();
350 #ifdef UNIFIED_COLLECTOR_TRACE_ENABLE
351 TraceStateMachine::GetInstance().SetTraceSwitchUcOff();
352 #ifdef HIVIEW_LOW_MEM_THRESHOLD
353 unifiedCollectorPtr->ExitCacheMonitorLoop();
354 #endif
355 #endif
356 unifiedCollectorPtr->CleanDataFiles();
357 }
358 }
359
InitWorkPath()360 void UnifiedCollector::InitWorkPath()
361 {
362 std::string hiviewWorkDir = GetHiviewContext()->GetHiViewDirectory(HiviewContext::DirectoryType::WORK_DIRECTORY);
363 const std::string uCollectionDirName = "unified_collection";
364 std::string tempWorkPath = FileUtil::IncludeTrailingPathDelimiter(hiviewWorkDir.append(uCollectionDirName));
365 if (!FileUtil::IsDirectory(tempWorkPath) && !FileUtil::ForceCreateDirectory(tempWorkPath)) {
366 HIVIEW_LOGE("failed to create dir=%{public}s", tempWorkPath.c_str());
367 return;
368 }
369 workPath_ = tempWorkPath;
370 }
371
RunCpuCollectionTask()372 void UnifiedCollector::RunCpuCollectionTask()
373 {
374 if (workPath_.empty() || isCpuTaskRunning_) {
375 HIVIEW_LOGE("workPath is null or task is running");
376 return;
377 }
378 isCpuTaskRunning_ = true;
379 auto task = [this] { this->CpuCollectionFfrtTask(); };
380 ffrt::submit(task, {}, {}, ffrt::task_attr().name("dft_uc_cpu").qos(ffrt::qos_default));
381 }
382
CpuCollectionFfrtTask()383 void UnifiedCollector::CpuCollectionFfrtTask()
384 {
385 cpuCollectionTask_ = std::make_shared<CpuCollectionTask>(workPath_);
386 while (true) {
387 if (!isCpuTaskRunning_) {
388 HIVIEW_LOGE("exit cpucollection task");
389 break;
390 }
391 ffrt::this_task::sleep_for(10s); // 10s: collect period
392 cpuCollectionTask_->Collect();
393 }
394 }
395
RunIoCollectionTask()396 void UnifiedCollector::RunIoCollectionTask()
397 {
398 if (workLoop_ == nullptr) {
399 HIVIEW_LOGE("workLoop is null");
400 return;
401 }
402 auto ioCollectionTask = [this] { this->IoCollectionTask(); };
403 const uint64_t taskInterval = 30; // 30s
404 auto ioSeqId = workLoop_->AddTimerEvent(nullptr, nullptr, ioCollectionTask, taskInterval, true);
405 taskList_.push_back(ioSeqId);
406 }
407
IoCollectionTask()408 void UnifiedCollector::IoCollectionTask()
409 {
410 auto ioCollector = UCollectUtil::IoCollector::Create();
411 (void)ioCollector->CollectDiskStats([](const DiskStats &stats) { return false; }, true);
412 (void)ioCollector->CollectAllProcIoStats(true);
413 }
414
RunUCollectionStatTask()415 void UnifiedCollector::RunUCollectionStatTask()
416 {
417 if (workLoop_ == nullptr) {
418 HIVIEW_LOGE("workLoop is null");
419 return;
420 }
421 auto statTask = [this] { this->UCollectionStatTask(); };
422 const uint64_t taskInterval = 600; // 600s
423 auto statSeqId = workLoop_->AddTimerEvent(nullptr, nullptr, statTask, taskInterval, true);
424 taskList_.push_back(statSeqId);
425 }
426
UCollectionStatTask()427 void UnifiedCollector::UCollectionStatTask()
428 {
429 UnifiedCollectionStat stat;
430 stat.Report();
431 }
432 } // namespace HiviewDFX
433 } // namespace OHOS
434