1 /*
2 * Copyright (C) 2021 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
16 #include "task_manager.h"
17 #include "common.h"
18 #include <chrono>
19 #include <sys/types.h>
20 #include <unistd.h>
21 #include "securec.h"
22
23 #include "include/heartbeat.h"
24 #include "include/sp_utils.h"
25 #include "include/sp_csv_util.h"
26 #include "include/sp_profiler_factory.h"
27 #include "include/sp_thread_socket.h"
28 #include "include/startup_delay.h"
29 #include "include/ByTrace.h"
30 #include "include/smartperf_command.h"
31 #include "include/sp_log.h"
32 #include "include/RAM.h"
33 #include "include/common.h"
34 #include "include/FPS.h"
35 #include "include/sp_task.h"
36 #include "include/CPU.h"
37 #include "include/navigation.h"
38 #include "include/AI_schedule.h"
39 #include "include/Threads.h"
40 #include "include/FileDescriptor.h"
41 #include "include/GpuCounter.h"
42 #include "include/hiperf.h"
43 #include "Capture.h"
44 #include <sys/syscall.h>
45 #include <filesystem>
46 #include "include/sdk_data_recv.h"
47
48 namespace {
49 OHOS::SmartPerf::TaskManager* g_mgr = nullptr;
50 thread_local OHOS::SmartPerf::ThreadLocal g_datas;
51 constexpr int32_t SAVE_DATA_INTERVAL_MINUTE = 5;
52 }
53
54 namespace OHOS::SmartPerf {
ThreadLocal()55 ThreadLocal::ThreadLocal()
56 {
57 if (g_mgr == nullptr) {
58 return;
59 }
60 g_mgr->RegisterThreadLocal(this);
61 }
62
~ThreadLocal()63 ThreadLocal::~ThreadLocal()
64 {
65 if (g_mgr == nullptr || (datasA_.empty() && datasB_.empty())) {
66 return;
67 }
68 g_mgr->CollectData(datasA_);
69 g_mgr->CollectData(datasB_);
70 }
71
TaskManager(bool isIPC)72 TaskManager::TaskManager(bool isIPC)
73 {
74 g_mgr = this;
75 if (isIPC) {
76 Capture::GetInstance().SocketMessage();
77 }
78 }
79
GpuCounterProcess(const ArgumentParser::ArgValue & value)80 void TaskManager::GpuCounterProcess(const ArgumentParser::ArgValue& value)
81 {
82 const int defaultFreq = 50;
83 const int minFreq = 50;
84 const int maxFreq = 1000;
85 if (std::holds_alternative<bool>(value)) {
86 // 如果val是bool类型,代表参数为-gc,使用默认频率
87 GpuCounter::GetInstance().SetFrequency(defaultFreq);
88 return;
89 }
90
91 // -GPU_COUNTER为int类型
92 // 参数拓展时,如果有其他类型,需要增加if判断,否则会variant识别异常导致crash
93 int frequency = std::get<int>(value);
94 // GPU_COUNTER频率最大支持10000,但daemon采集间隔是1000。频率设置应小于采集间隔。
95 if (frequency <= maxFreq && frequency >= minFreq && frequency % minFreq == 0) {
96 GpuCounter::GetInstance().SetFrequency(frequency);
97 } else {
98 LOGW("GPU_COUNTER frequency must be a factor of 50 and in range [50,1000], " +
99 "this frequency is %d, set to 50", frequency);
100 GpuCounter::GetInstance().SetFrequency(defaultFreq);
101 }
102 }
103
SpecialKeyProcess(const std::string specKey)104 void TaskManager::SpecialKeyProcess(const std::string specKey)
105 {
106 if (specKey == "-LOW_POWER") {
107 FPS::GetInstance().hapLowFpsFlag = true;
108 }
109 if (specKey == "-fc") {
110 FPS::GetInstance().hapNeedCatonInfo = true;
111 }
112 }
113
AddTask(const std::unordered_map<std::string,ArgumentParser::ArgValue> & argv)114 void TaskManager::AddTask(const std::unordered_map<std::string, ArgumentParser::ArgValue>& argv)
115 {
116 for (auto& [key, val] : argv) {
117 auto iter = COMMAND_MAP.find(key);
118 if (iter == COMMAND_MAP.end()) {
119 iter = COMMAND_SHELL_MAP.find(key);
120 }
121 if (iter == COMMAND_MAP.end() || iter == COMMAND_SHELL_MAP.end()) {
122 continue;
123 }
124 SpecialKeyProcess(key);
125 HandleCommandType(iter->second, val);
126
127 SpProfiler* pro = SpProfilerFactory::GetCmdProfilerItem(iter->second, true);
128 if (pro == nullptr) {
129 continue;
130 }
131 pro->ipcCallback_ = ipcCallback_;
132 if (iter->second == CommandType::CT_C || iter->second == CommandType::CT_P) {
133 priorityTask_.insert(pro);
134 } else {
135 normalTask_.insert(pro);
136 }
137 }
138 }
139
HandleCommandType(CommandType cmdType,const ArgumentParser::ArgValue & val)140 void TaskManager::HandleCommandType(CommandType cmdType, const ArgumentParser::ArgValue& val)
141 {
142 switch (cmdType) {
143 case CommandType::CT_N: {
144 collectCount_ = std::get<int>(val);
145 break;
146 }
147 case CommandType::CT_PKG:
148 case CommandType::CT_PID: {
149 GetProcessInfo(cmdType, val);
150 break;
151 }
152 case CommandType::CT_PRINT: {
153 printDataInfo_ = true;
154 break;
155 }
156 case CommandType::CT_VIEW: {
157 FPS::GetInstance().SetLayerName(std::get<std::string>(val));
158 break;
159 }
160 case CommandType::CT_OUT: {
161 SetFilePath(std::get<std::string>(val));
162 break;
163 }
164 case CommandType::CT_GC: {
165 GpuCounterProcess(val);
166 break;
167 }
168 default:
169 break;
170 }
171 }
172
AddTask(const std::string & argv)173 void TaskManager::AddTask(const std::string& argv)
174 {
175 LOGD("AddTask argv (%s)", argv.c_str());
176 parameter_.Parse(argv);
177 AddTask(parameter_.Values());
178 }
179
AddTask(SpProfiler * task,bool priority)180 void TaskManager::AddTask(SpProfiler* task, bool priority)
181 {
182 if (task == nullptr) {
183 return;
184 }
185 LOGD("Start the collection in the start/stop mode");
186 priority ? priorityTask_.insert(task) : normalTask_.insert(task);
187 }
188
AddTask(std::vector<std::string> & argv)189 void TaskManager::AddTask(std::vector<std::string>& argv)
190 {
191 std::string argvStr;
192 for (auto& item : argv) {
193 argvStr += item + " ";
194 }
195 AddTask(argvStr);
196 }
197
SetFilePath(const std::string & fileName,bool removeCurrentFile)198 void TaskManager::SetFilePath(const std::string& fileName, bool removeCurrentFile)
199 {
200 fileName_ = fileName;
201 saveFlag_ = true;
202 if (removeCurrentFile) {
203 std::remove(fileName_.c_str());
204 }
205 std::filesystem::path fullPath = fileName;
206 std::string dir = fullPath.parent_path().string();
207 GpuCounter::GetInstance().SetSavePathDirectory(dir);
208 SdkDataRecv::GetInstance().SetFilePath(dir);
209 }
210
TaskFun(SpProfiler * pro,uint32_t batch,bool record)211 std::map<std::string, std::string> TaskManager::TaskFun(SpProfiler* pro, uint32_t batch, bool record)
212 {
213 auto mapRes = pro->ItemData();
214 if (record) {
215 if (g_datas.switch_) {
216 g_datas.datasA_[batch].insert(mapRes.begin(), mapRes.end());
217 } else {
218 g_datas.datasB_[batch].insert(mapRes.begin(), mapRes.end());
219 }
220 }
221 return mapRes;
222 }
223
CollectThreadsData()224 void TaskManager::CollectThreadsData()
225 {
226 for (auto& item : threadLocals_) {
227 if (item == nullptr) {
228 continue;
229 }
230
231 std::map<uint32_t, std::map<std::string, std::string>>& datas =
232 (running_ ^ item->switch_) ? item->datasA_ : item->datasB_;
233
234 CollectData(datas);
235 std::map<uint32_t, std::map<std::string, std::string>>().swap(datas);
236 }
237 }
238
StartSaveFileThread()239 void TaskManager::StartSaveFileThread()
240 {
241 if (scheduleSaveDataTh_.joinable()) {
242 LOGD("The thread save data is working");
243 return;
244 }
245 scheduleSaveDataTh_ = std::thread([this]() {
246 LOGD("running: %d, recordData: %d", running_.load(), recordData_.load());
247 while (running_ && recordData_) {
248 std::unique_lock<std::mutex> lock(scheduleSaveDataMtx_);
249 scheduleSaveDataCond_.wait(lock);
250 CollectThreadsData();
251 WriteToCSV();
252 std::map<uint32_t, std::map<std::string, std::string>>().swap(datas_);
253 savingFile_ = false;
254 }
255 LOGD("The data saving thread exits");
256 });
257 }
258
SaveRegularly(std::chrono::steady_clock::time_point & loopEnd)259 void TaskManager::SaveRegularly(std::chrono::steady_clock::time_point& loopEnd)
260 {
261 if (!recordData_) {
262 return;
263 }
264
265 if ((loopEnd - currentTimePoint_) >= std::chrono::minutes(SAVE_DATA_INTERVAL_MINUTE) &&
266 !(SpProfilerFactory::editorFlag)) {
267 std::unique_lock<std::mutex> lock(mtx_);
268 if (savingFile_) {
269 LOGD("Saving file");
270 return;
271 }
272 savingFile_ = true;
273 for (auto& item : threadLocals_) {
274 if (item == nullptr) {
275 continue;
276 }
277 item->switch_ = !item->switch_;
278 }
279 LOGD("Start saving data automatically");
280 scheduleSaveDataCond_.notify_all();
281 currentTimePoint_ = loopEnd;
282 }
283 }
284
Start(bool record)285 void TaskManager::Start(bool record)
286 {
287 if (priorityTask_.empty() && normalTask_.empty()) {
288 return;
289 }
290 ProcessOnceTask(true);
291 running_ = true;
292 if (record) {
293 saveFlag_ = true;
294 SetRecordState(true);
295 }
296 mainLoop_ = std::thread([this]() {
297 MainLoop();
298 });
299 }
300
MainLoop()301 void TaskManager::MainLoop()
302 {
303 while (running_) {
304 auto loopStart = std::chrono::steady_clock::now();
305 bool recordData = recordData_.load();
306 if (dataIndex_++ == collectCount_) {
307 break;
308 }
309 LOGD("data index: %u", dataIndex_);
310 std::map<std::string, std::string> currDatas;
311 currDatas.emplace("timestamp", std::to_string(SPUtils::GetCurTime()));
312 if (recordData) {
313 datas_.emplace(dataIndex_,
314 std::map<std::string, std::string>{{"timestamp", std::to_string(SPUtils::GetCurTime())}});
315 }
316 for (auto& item : priorityTask_) {
317 currDatas.merge(threadPool_.PushTask(&TaskManager::TaskFun, this, item, dataIndex_, recordData).get());
318 }
319 std::vector<std::future<std::map<std::string, std::string>>> result;
320 for (auto& item : normalTask_) {
321 result.emplace_back(threadPool_.PushTask(&TaskManager::TaskFun, this, item, dataIndex_, recordData));
322 }
323 for (auto& item : result) {
324 currDatas.merge(item.get());
325 }
326 for (auto& item : OHOS::SmartPerf::GpuCounter::GetInstance().GetGpuRealtimeData()) {
327 currDatas.insert(item);
328 }
329 if (nextTime_ != nullptr) {
330 *nextTime_ = SPUtils::GetCurTime();
331 }
332 ProcessCurrentBatch(currDatas);
333 auto loopEnd = std::chrono::steady_clock::now();
334 SaveRegularly(loopEnd);
335 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(loopEnd - loopStart);
336 if (elapsed < std::chrono::seconds(1)) {
337 std::this_thread::sleep_for(std::chrono::seconds(1) - elapsed);
338 }
339 }
340 LOGD("main loop exit");
341 ProcessOnceTask(false);
342 running_ = false;
343 finishCond_.notify_all();
344 scheduleSaveDataCond_.notify_all();
345 }
346
WriteToCSV()347 void TaskManager::WriteToCSV()
348 {
349 std::unique_lock<std::mutex> lock(mtx_);
350 if (datas_.empty() || !saveFlag_) {
351 LOGD("datas is empty or saveFlag: %d", saveFlag_);
352 return;
353 }
354 if (!dataFile_.is_open()) {
355 char filePath[PATH_MAX] = {0};
356 if (realpath(fileName_.c_str(), filePath) == nullptr) {
357 if (strncpy_s(filePath, PATH_MAX, fileName_.c_str(), fileName_.size()) != 0) {
358 LOGE("strncpy_s failed");
359 return;
360 }
361 LOGE("The file %s does not exist, will create", fileName_.c_str());
362 }
363 dataFile_.open(filePath, std::ios::out | std::ios::app);
364 if (!dataFile_.is_open()) {
365 LOGE("The file open fail");
366 return;
367 }
368 LOGD("The file will write data size = (%u)", datas_.size());
369 SetFileTitle();
370 }
371 for (auto& [_, v] : datas_) {
372 for (auto& item : titles_) {
373 auto it = v.find(item);
374 if (it != v.end()) {
375 dataFile_ << it->second;
376 }
377 dataFile_ << ",";
378 }
379 dataFile_ << "\n";
380 }
381 dataFile_.close();
382
383 if (!running_) {
384 std::map<uint32_t, std::map<std::string, std::string>>().swap(datas_);
385 }
386 }
387
SetFileTitle()388 void TaskManager::SetFileTitle()
389 {
390 if (firstSetTitle_) {
391 for (const auto& [id, m] : datas_) {
392 for (const auto& [k, _] : m) {
393 titles_.insert(k);
394 }
395 }
396 for (const auto& field : titles_) {
397 dataFile_ << field << ",";
398 }
399 dataFile_ << "\n";
400 }
401 firstSetTitle_ = false;
402 }
403
Stop(bool pause)404 void TaskManager::Stop(bool pause)
405 {
406 isPause_ = pause;
407 running_ = false;
408 if (mainLoop_.joinable()) {
409 {
410 std::lock_guard<std::mutex> lock(mtx_);
411 running_ = false;
412 }
413 mainLoop_.join();
414 }
415 if (scheduleSaveDataTh_.joinable()) {
416 {
417 std::lock_guard<std::mutex> lock(mtx_);
418 running_ = false;
419 }
420 scheduleSaveDataCond_.notify_all();
421 scheduleSaveDataTh_.join();
422 }
423 if (!pause) {
424 LOGD("Start/Stop Collection End");
425 threadPool_.Stop();
426 }
427 }
428
Wait()429 void TaskManager::Wait()
430 {
431 std::unique_lock<std::mutex> lock(finishMtx_);
432 finishCond_.wait(lock, [this] { return !running_.load(); });
433 }
434
CollectData(std::map<uint32_t,std::map<std::string,std::string>> & datas)435 void TaskManager::CollectData(std::map<uint32_t, std::map<std::string, std::string>>& datas)
436 {
437 std::unique_lock<std::mutex> lock(mtx_);
438 for (auto& [k, v] : datas) {
439 if (v.empty()) {
440 continue;
441 }
442 datas_[k].merge(v);
443 }
444 }
445
GetProcessInfo(CommandType type,const ArgumentParser::ArgValue & value)446 void TaskManager::GetProcessInfo(CommandType type, const ArgumentParser::ArgValue& value)
447 {
448 if (type == CommandType::CT_PKG) {
449 processName_ = std::get<std::string>(value);
450 OHOS::SmartPerf::StartUpDelay sp;
451 sp.GetPidByPkg(processName_, &processIds_);
452 }
453
454 if (type == CommandType::CT_PID) {
455 processIds_ = std::to_string(std::get<int32_t>(value));
456 const std::string getProcPkg = "cat /proc/" + processIds_ + "/cmdline";
457 FILE *fd = popen(getProcPkg.c_str(), "r");
458 if (fd == nullptr) {
459 return;
460 }
461 char buf[1024] = {'\0'};
462 while ((fgets(buf, sizeof(buf), fd)) != nullptr) {
463 processName_ = buf;
464 }
465 if (pclose(fd) == -1) {
466 LOGE("Error: Failed to close file");
467 return;
468 }
469 }
470
471 FPS::GetInstance().SetProcessId(processIds_);
472 FPS::GetInstance().SetPackageName(processName_);
473 RAM::GetInstance().SetProcessId(processIds_);
474 RAM::GetInstance().SetPackageName(processName_);
475 CPU::GetInstance().SetProcessId(processIds_);
476 CPU::GetInstance().SetPackageName(processName_);
477 Navigation::GetInstance().SetProcessId(processIds_);
478 AISchedule::GetInstance().SetProcessId(processIds_);
479 Threads::GetInstance().SetProcessId(processIds_);
480 Threads::GetInstance().SetPackageName(processName_);
481 FileDescriptor::GetInstance().SetProcessId(processIds_);
482 FileDescriptor::GetInstance().SetPackageName(processName_);
483 Hiperf::GetInstance().SetProcessId(processIds_);
484 }
485
RegisterThreadLocal(ThreadLocal * local)486 void TaskManager::RegisterThreadLocal(ThreadLocal* local)
487 {
488 std::unique_lock<std::mutex> lock(mtx_);
489 threadLocals_.emplace_back(local);
490 }
491
MapToString(std::map<std::string,std::string> & myMap)492 std::string TaskManager::MapToString(std::map<std::string, std::string>& myMap)
493 {
494 if (hapCollect_) {
495 std::string appCollectMap = "t_index_info$$";
496 std::vector<std::string> keysToFind = {"fps", "refreshrate", "currentNow", "ddrFrequency", "cpu0Frequency",
497 "cpu1Frequency", "cpu2Frequency", "cpu3Frequency", "cpu4Frequency", "cpu5Frequency", "cpu6Frequency",
498 "cpu7Frequency", "cpu8Frequency", "cpu9Frequency", "cpu10Frequency", "cpu11Frequency", "gpuFrequency",
499 "pss", "shell_frame", "shell_back", "soc_thermal", "cpu0Usage", "cpu1Usage", "cpu2Usage", "cpu3Usage",
500 "cpu4Usage", "cpu5Usage", "cpu6Usage", "cpu7Usage", "cpu8Usage", "cpu9Usage", "cpu10Usage", "cpu11Usage",
501 "gpuLoad"};
502 for (const auto& key : keysToFind) {
503 if (auto iter = myMap.find(key); iter != myMap.end()) {
504 appCollectMap += iter->first + ":" + iter->second + ",";
505 }
506 }
507 return appCollectMap;
508 } else {
509 std::string str = "{ ";
510 for (auto it = myMap.begin(); it != myMap.end(); ++it) {
511 str += "\"" + it->first + "\": " + it->second + ", ";
512 }
513 const int subLen = 2;
514 str.erase(str.end() - subLen, str.end());
515 str += " }";
516 return str;
517 }
518 }
519
ProcessCurrentBatch(std::map<std::string,std::string> & data)520 void TaskManager::ProcessCurrentBatch(std::map<std::string, std::string>& data)
521 {
522 if (printDataInfo_) {
523 std::cerr << std::endl;
524 uint32_t index = 0;
525 for (auto& [k, v] : data) {
526 std::cerr << "order:" << index++ << " ";
527 std::cerr << k << "=" << v << std::endl;
528 }
529 }
530
531 if (ipcDataRecv_) {
532 ipcCallback_(MapToString(data));
533 }
534 }
535
EnableIPCCallback()536 void TaskManager::EnableIPCCallback()
537 {
538 ipcDataRecv_ = true;
539 }
540
SetIPCCallback(std::function<void (const std::string &)> callback)541 void TaskManager::SetIPCCallback(std::function<void(const std::string&)> callback)
542 {
543 ipcCallback_ = callback;
544 }
545
DisableIPCCallback()546 void TaskManager::DisableIPCCallback()
547 {
548 ipcDataRecv_ = false;
549 }
550
SetHapFlag(bool flag)551 void TaskManager::SetHapFlag(bool flag)
552 {
553 hapCollect_ = flag;
554 }
555
SetNextTime(long long & nextTime)556 void TaskManager::SetNextTime(long long& nextTime)
557 {
558 nextTime_ = &nextTime;
559 }
560
ProcessOnceTask(bool start)561 void TaskManager::ProcessOnceTask(bool start)
562 {
563 for (auto& item : priorityTask_) {
564 if (item == nullptr) {
565 continue;
566 }
567 start ? item->StartExecutionOnce(isPause_.load()) : item->FinishtExecutionOnce(isPause_.load());
568 }
569
570 for (auto& item : normalTask_) {
571 if (item == nullptr) {
572 continue;
573 }
574 start ? item->StartExecutionOnce(isPause_.load()) : item->FinishtExecutionOnce(isPause_.load());
575 }
576 }
577
SetRecordState(bool record)578 void TaskManager::SetRecordState(bool record)
579 {
580 if (record) {
581 LOGD("Start saving data regularly");
582 currentTimePoint_ = std::chrono::steady_clock::now();
583 recordData_ = true;
584 StartSaveFileThread();
585 } else {
586 LOGD("Turn off timing to save data");
587 auto time = currentTimePoint_ + std::chrono::minutes(SAVE_DATA_INTERVAL_MINUTE + 1);
588 SaveRegularly(time);
589 recordData_ = false;
590
591 if (scheduleSaveDataTh_.joinable()) {
592 scheduleSaveDataTh_.join();
593 }
594 }
595 }
596
DeleteTask(SpProfiler * task)597 void TaskManager::DeleteTask(SpProfiler* task)
598 {
599 if (task == nullptr) {
600 return;
601 }
602
603 for (auto iter = normalTask_.begin(); iter != normalTask_.end(); ++iter) {
604 if (task == *iter) {
605 normalTask_.erase(iter);
606 return;
607 }
608 }
609 for (auto iter = priorityTask_.begin(); iter != priorityTask_.end(); ++iter) {
610 if (task == *iter) {
611 priorityTask_.erase(iter);
612 return;
613 }
614 }
615 }
616
InitDataCsv()617 void TaskManager::InitDataCsv()
618 {
619 if (!SPUtils::FileAccess(fileName_)) {
620 std::ofstream file(fileName_);
621 if (file) {
622 std::string chmodCmd = "chmod 777 " + fileName_;
623 std::string cmdResult;
624 SPUtils::LoadCmd(chmodCmd, cmdResult);
625 file.close();
626 } else {
627 LOGE("Failed to creat file");
628 return;
629 }
630 } else {
631 std::ofstream file(fileName_, std::ios::trunc);
632 if (!file) {
633 LOGE("Unable to open file");
634 return;
635 }
636 file.close();
637 LOGD("CreatPath already exists: %s", fileName_.c_str());
638 }
639 }
640
GetArgumentParser()641 ArgumentParser& TaskManager::GetArgumentParser()
642 {
643 return parameter_;
644 }
645 }