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 continue;
120 }
121 SpecialKeyProcess(key);
122 switch (iter->second) {
123 case CommandType::CT_N: {
124 collectCount_ = std::get<int>(val);
125 continue;
126 }
127 case CommandType::CT_PKG:
128 case CommandType::CT_PID: {
129 GetProcessInfo(iter->second, val);
130 continue;
131 }
132 case CommandType::CT_PRINT: {
133 printDataInfo_ = true;
134 continue;
135 }
136 case CommandType::CT_VIEW: {
137 FPS::GetInstance().SetLayerName(std::get<std::string>(val));
138 continue;
139 }
140 case CommandType::CT_OUT: {
141 SetFilePath(std::get<std::string>(val));
142 continue;
143 }
144 case CommandType::CT_GC: {
145 GpuCounterProcess(val);
146 break;
147 }
148 default:
149 break;
150 }
151
152 SpProfiler* pro = SpProfilerFactory::GetCmdProfilerItem(iter->second, true);
153 if (pro == nullptr) {
154 continue;
155 }
156 pro->ipcCallback_ = ipcCallback_;
157 if (iter->second == CommandType::CT_C || iter->second == CommandType::CT_P) {
158 priorityTask_.insert(pro);
159 } else {
160 normalTask_.insert(pro);
161 }
162 }
163 }
164
AddTask(const std::string & argv)165 void TaskManager::AddTask(const std::string& argv)
166 {
167 LOGD("AddTask argv (%s)", argv.c_str());
168 parameter_.Parse(argv);
169 AddTask(parameter_.Values());
170 }
171
AddTask(SpProfiler * task,bool priority)172 void TaskManager::AddTask(SpProfiler* task, bool priority)
173 {
174 if (task == nullptr) {
175 return;
176 }
177 LOGD("Start the collection in the start/stop mode");
178 priority ? priorityTask_.insert(task) : normalTask_.insert(task);
179 }
180
AddTask(std::vector<std::string> & argv)181 void TaskManager::AddTask(std::vector<std::string>& argv)
182 {
183 std::string argvStr;
184 for (auto& item : argv) {
185 argvStr += item + " ";
186 }
187 AddTask(argvStr);
188 }
189
SetFilePath(const std::string & fileName,bool removeCurrentFile)190 void TaskManager::SetFilePath(const std::string& fileName, bool removeCurrentFile)
191 {
192 fileName_ = fileName;
193 saveFlag_ = true;
194 if (removeCurrentFile) {
195 std::remove(fileName_.c_str());
196 }
197 std::filesystem::path fullPath = fileName;
198 std::string dir = fullPath.parent_path().string();
199 GpuCounter::GetInstance().SetSavePathDirectory(dir);
200 SdkDataRecv::GetInstance().SetFilePath(dir);
201 }
202
TaskFun(SpProfiler * pro,uint32_t batch,bool record)203 std::map<std::string, std::string> TaskManager::TaskFun(SpProfiler* pro, uint32_t batch, bool record)
204 {
205 auto mapRes = pro->ItemData();
206 if (record) {
207 if (g_datas.switch_) {
208 g_datas.datasA_[batch].insert(mapRes.begin(), mapRes.end());
209 } else {
210 g_datas.datasB_[batch].insert(mapRes.begin(), mapRes.end());
211 }
212 }
213 return mapRes;
214 }
215
CollectThreadsData()216 void TaskManager::CollectThreadsData()
217 {
218 for (auto& item : threadLocals_) {
219 if (item == nullptr) {
220 continue;
221 }
222
223 std::map<uint32_t, std::map<std::string, std::string>>& datas =
224 (running_ ^ item->switch_) ? item->datasA_ : item->datasB_;
225
226 CollectData(datas);
227 std::map<uint32_t, std::map<std::string, std::string>>().swap(datas);
228 }
229 }
230
StartSaveFileThread()231 void TaskManager::StartSaveFileThread()
232 {
233 if (scheduleSaveDataTh_.joinable()) {
234 LOGD("The thread save data is working");
235 return;
236 }
237 scheduleSaveDataTh_ = std::thread([this]() {
238 LOGD("running: %d, recordData: %d", running_.load(), recordData_.load());
239 while (running_ && recordData_) {
240 std::unique_lock<std::mutex> lock(scheduleSaveDataMtx_);
241 scheduleSaveDataCond_.wait(lock);
242 CollectThreadsData();
243 WriteToCSV();
244 std::map<uint32_t, std::map<std::string, std::string>>().swap(datas_);
245 savingFile_ = false;
246 }
247 LOGD("The data saving thread exits");
248 });
249 }
250
SaveRegularly(std::chrono::steady_clock::time_point & loopEnd)251 void TaskManager::SaveRegularly(std::chrono::steady_clock::time_point& loopEnd)
252 {
253 if (!recordData_) {
254 return;
255 }
256
257 if ((loopEnd - currentTimePoint_) >= std::chrono::minutes(SAVE_DATA_INTERVAL_MINUTE) &&
258 !(SpProfilerFactory::editorFlag)) {
259 std::unique_lock<std::mutex> lock(mtx_);
260 if (savingFile_) {
261 LOGD("Saving file");
262 return;
263 }
264 savingFile_ = true;
265 for (auto& item : threadLocals_) {
266 if (item == nullptr) {
267 continue;
268 }
269 item->switch_ = !item->switch_;
270 }
271 LOGD("Start saving data automatically");
272 scheduleSaveDataCond_.notify_all();
273 currentTimePoint_ = loopEnd;
274 }
275 }
276
Start(bool record)277 void TaskManager::Start(bool record)
278 {
279 if (priorityTask_.empty() && normalTask_.empty()) {
280 return;
281 }
282 ProcessOnceTask(true);
283 running_ = true;
284 if (record) {
285 saveFlag_ = true;
286 SetRecordState(true);
287 }
288 mainLoop_ = std::thread([this]() {
289 MainLoop();
290 });
291 }
292
MainLoop()293 void TaskManager::MainLoop()
294 {
295 while (running_) {
296 auto loopStart = std::chrono::steady_clock::now();
297 bool recordData = recordData_.load();
298 if (dataIndex_++ == collectCount_) {
299 break;
300 }
301 LOGD("data index: %u", dataIndex_);
302 std::map<std::string, std::string> currDatas;
303 currDatas.emplace("timestamp", std::to_string(SPUtils::GetCurTime()));
304 if (recordData) {
305 datas_.emplace(dataIndex_,
306 std::map<std::string, std::string>{{"timestamp", std::to_string(SPUtils::GetCurTime())}});
307 }
308 for (auto& item : priorityTask_) {
309 currDatas.merge(threadPool_.PushTask(&TaskManager::TaskFun, this, item, dataIndex_, recordData).get());
310 }
311 std::vector<std::future<std::map<std::string, std::string>>> result;
312 for (auto& item : normalTask_) {
313 result.emplace_back(threadPool_.PushTask(&TaskManager::TaskFun, this, item, dataIndex_, recordData));
314 }
315 for (auto& item : result) {
316 currDatas.merge(item.get());
317 }
318 for (auto& item : OHOS::SmartPerf::GpuCounter::GetInstance().GetGpuRealtimeData()) {
319 currDatas.insert(item);
320 }
321 if (nextTime_ != nullptr) {
322 *nextTime_ = SPUtils::GetCurTime();
323 }
324 ProcessCurrentBatch(currDatas);
325 auto loopEnd = std::chrono::steady_clock::now();
326 SaveRegularly(loopEnd);
327 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(loopEnd - loopStart);
328 if (elapsed < std::chrono::seconds(1)) {
329 std::this_thread::sleep_for(std::chrono::seconds(1) - elapsed);
330 }
331 }
332 LOGD("main loop exit");
333 ProcessOnceTask(false);
334 running_ = false;
335 finishCond_.notify_all();
336 scheduleSaveDataCond_.notify_all();
337 }
338
WriteToCSV()339 void TaskManager::WriteToCSV()
340 {
341 std::unique_lock<std::mutex> lock(mtx_);
342 if (datas_.empty() || !saveFlag_) {
343 LOGD("datas is empty or saveFlag: %d", saveFlag_);
344 return;
345 }
346 if (!dataFile_.is_open()) {
347 char filePath[PATH_MAX] = {0};
348 if (realpath(fileName_.c_str(), filePath) == nullptr) {
349 if (strncpy_s(filePath, PATH_MAX, fileName_.c_str(), fileName_.size()) != 0) {
350 LOGE("strncpy_s failed");
351 return;
352 }
353 LOGE("The file %s does not exist, will create", fileName_.c_str());
354 }
355 dataFile_.open(filePath, std::ios::out | std::ios::app);
356 if (!dataFile_.is_open()) {
357 LOGE("The file open fail");
358 return;
359 }
360 LOGD("The file will write data size = (%u)", datas_.size());
361 SetFileTitle();
362 }
363 for (auto& [_, v] : datas_) {
364 for (auto& item : titles_) {
365 auto it = v.find(item);
366 if (it != v.end()) {
367 dataFile_ << it->second;
368 }
369 dataFile_ << ",";
370 }
371 dataFile_ << "\n";
372 }
373 dataFile_.close();
374
375 if (!running_) {
376 std::map<uint32_t, std::map<std::string, std::string>>().swap(datas_);
377 }
378 }
379
SetFileTitle()380 void TaskManager::SetFileTitle()
381 {
382 if (firstSetTitle_) {
383 for (const auto& [id, m] : datas_) {
384 for (const auto& [k, _] : m) {
385 titles_.insert(k);
386 }
387 }
388 for (const auto& field : titles_) {
389 dataFile_ << field << ",";
390 }
391 dataFile_ << "\n";
392 }
393 firstSetTitle_ = false;
394 }
395
Stop(bool pause)396 void TaskManager::Stop(bool pause)
397 {
398 isPause_ = pause;
399 running_ = false;
400 if (mainLoop_.joinable()) {
401 {
402 std::lock_guard<std::mutex> lock(mtx_);
403 running_ = false;
404 }
405 mainLoop_.join();
406 }
407 if (scheduleSaveDataTh_.joinable()) {
408 {
409 std::lock_guard<std::mutex> lock(mtx_);
410 running_ = false;
411 }
412 scheduleSaveDataCond_.notify_all();
413 scheduleSaveDataTh_.join();
414 }
415 if (!pause) {
416 LOGD("Start/Stop Collection End");
417 threadPool_.Stop();
418 }
419 }
420
Wait()421 void TaskManager::Wait()
422 {
423 std::unique_lock<std::mutex> lock(finishMtx_);
424 finishCond_.wait(lock, [this] { return !running_.load(); });
425 }
426
CollectData(std::map<uint32_t,std::map<std::string,std::string>> & datas)427 void TaskManager::CollectData(std::map<uint32_t, std::map<std::string, std::string>>& datas)
428 {
429 std::unique_lock<std::mutex> lock(mtx_);
430 for (auto& [k, v] : datas) {
431 if (v.empty()) {
432 continue;
433 }
434 datas_[k].merge(v);
435 }
436 }
437
GetProcessInfo(CommandType type,const ArgumentParser::ArgValue & value)438 void TaskManager::GetProcessInfo(CommandType type, const ArgumentParser::ArgValue& value)
439 {
440 if (type == CommandType::CT_PKG) {
441 processName_ = std::get<std::string>(value);
442 OHOS::SmartPerf::StartUpDelay sp;
443 sp.GetPidByPkg(processName_, &processIds_);
444 }
445
446 if (type == CommandType::CT_PID) {
447 processIds_ = std::to_string(std::get<int32_t>(value));
448 const std::string getProcPkg = "cat /proc/" + processIds_ + "/cmdline";
449 FILE *fd = popen(getProcPkg.c_str(), "r");
450 if (fd == nullptr) {
451 return;
452 }
453 char buf[1024] = {'\0'};
454 while ((fgets(buf, sizeof(buf), fd)) != nullptr) {
455 processName_ = buf;
456 }
457 if (pclose(fd) == -1) {
458 LOGE("Error: Failed to close file");
459 return;
460 }
461 }
462
463 FPS::GetInstance().SetProcessId(processIds_);
464 FPS::GetInstance().SetPackageName(processName_);
465 RAM::GetInstance().SetProcessId(processIds_);
466 RAM::GetInstance().SetPackageName(processName_);
467 CPU::GetInstance().SetProcessId(processIds_);
468 CPU::GetInstance().SetPackageName(processName_);
469 Navigation::GetInstance().SetProcessId(processIds_);
470 AISchedule::GetInstance().SetProcessId(processIds_);
471 Threads::GetInstance().SetProcessId(processIds_);
472 Threads::GetInstance().SetPackageName(processName_);
473 FileDescriptor::GetInstance().SetProcessId(processIds_);
474 FileDescriptor::GetInstance().SetPackageName(processName_);
475 Hiperf::GetInstance().SetProcessId(processIds_);
476 }
477
RegisterThreadLocal(ThreadLocal * local)478 void TaskManager::RegisterThreadLocal(ThreadLocal* local)
479 {
480 std::unique_lock<std::mutex> lock(mtx_);
481 threadLocals_.emplace_back(local);
482 }
483
MapToString(std::map<std::string,std::string> & myMap)484 std::string TaskManager::MapToString(std::map<std::string, std::string>& myMap)
485 {
486 if (hapCollect_) {
487 std::string appCollectMap = "t_index_info$$";
488 std::vector<std::string> keysToFind = {"fps", "refreshrate", "currentNow", "ddrFrequency", "cpu0Frequency",
489 "cpu1Frequency", "cpu2Frequency", "cpu3Frequency", "cpu4Frequency", "cpu5Frequency", "cpu6Frequency",
490 "cpu7Frequency", "cpu8Frequency", "cpu9Frequency", "cpu10Frequency", "cpu11Frequency", "gpuFrequency",
491 "pss", "shell_frame", "shell_back", "soc_thermal", "cpu0Usage", "cpu1Usage", "cpu2Usage", "cpu3Usage",
492 "cpu4Usage", "cpu5Usage", "cpu6Usage", "cpu7Usage", "cpu8Usage", "cpu9Usage", "cpu10Usage", "cpu11Usage",
493 "gpuLoad"};
494 for (const auto& key : keysToFind) {
495 if (auto iter = myMap.find(key); iter != myMap.end()) {
496 appCollectMap += iter->first + ":" + iter->second + ",";
497 }
498 }
499 return appCollectMap;
500 } else {
501 std::string str = "{ ";
502 for (auto it = myMap.begin(); it != myMap.end(); ++it) {
503 str += "\"" + it->first + "\": " + it->second + ", ";
504 }
505 const int subLen = 2;
506 str.erase(str.end() - subLen, str.end());
507 str += " }";
508 return str;
509 }
510 }
511
ProcessCurrentBatch(std::map<std::string,std::string> & data)512 void TaskManager::ProcessCurrentBatch(std::map<std::string, std::string>& data)
513 {
514 if (printDataInfo_) {
515 std::cerr << std::endl;
516 uint32_t index = 0;
517 for (auto& [k, v] : data) {
518 std::cerr << "order:" << index++ << " ";
519 std::cerr << k << "=" << v << std::endl;
520 }
521 }
522
523 if (ipcDataRecv_) {
524 ipcCallback_(MapToString(data));
525 }
526 }
527
EnableIPCCallback()528 void TaskManager::EnableIPCCallback()
529 {
530 ipcDataRecv_ = true;
531 }
532
SetIPCCallback(std::function<void (const std::string &)> callback)533 void TaskManager::SetIPCCallback(std::function<void(const std::string&)> callback)
534 {
535 ipcCallback_ = callback;
536 }
537
DisableIPCCallback()538 void TaskManager::DisableIPCCallback()
539 {
540 ipcDataRecv_ = false;
541 }
542
SetHapFlag(bool flag)543 void TaskManager::SetHapFlag(bool flag)
544 {
545 hapCollect_ = flag;
546 }
547
SetNextTime(long long & nextTime)548 void TaskManager::SetNextTime(long long& nextTime)
549 {
550 nextTime_ = &nextTime;
551 }
552
ProcessOnceTask(bool start)553 void TaskManager::ProcessOnceTask(bool start)
554 {
555 for (auto& item : priorityTask_) {
556 if (item == nullptr) {
557 continue;
558 }
559 start ? item->StartExecutionOnce(isPause_.load()) : item->FinishtExecutionOnce(isPause_.load());
560 }
561
562 for (auto& item : normalTask_) {
563 if (item == nullptr) {
564 continue;
565 }
566 start ? item->StartExecutionOnce(isPause_.load()) : item->FinishtExecutionOnce(isPause_.load());
567 }
568 }
569
SetRecordState(bool record)570 void TaskManager::SetRecordState(bool record)
571 {
572 if (record) {
573 LOGD("Start saving data regularly");
574 currentTimePoint_ = std::chrono::steady_clock::now();
575 recordData_ = true;
576 StartSaveFileThread();
577 } else {
578 LOGD("Turn off timing to save data");
579 auto time = currentTimePoint_ + std::chrono::minutes(SAVE_DATA_INTERVAL_MINUTE + 1);
580 SaveRegularly(time);
581 recordData_ = false;
582
583 if (scheduleSaveDataTh_.joinable()) {
584 scheduleSaveDataTh_.join();
585 }
586 }
587 }
588
DeleteTask(SpProfiler * task)589 void TaskManager::DeleteTask(SpProfiler* task)
590 {
591 if (task == nullptr) {
592 return;
593 }
594
595 for (auto iter = normalTask_.begin(); iter != normalTask_.end(); ++iter) {
596 if (task == *iter) {
597 normalTask_.erase(iter);
598 return;
599 }
600 }
601 for (auto iter = priorityTask_.begin(); iter != priorityTask_.end(); ++iter) {
602 if (task == *iter) {
603 priorityTask_.erase(iter);
604 return;
605 }
606 }
607 }
608
InitDataCsv()609 void TaskManager::InitDataCsv()
610 {
611 if (!SPUtils::FileAccess(fileName_)) {
612 std::ofstream file(fileName_);
613 if (file) {
614 std::string chmodCmd = "chmod 777 " + fileName_;
615 std::string cmdResult;
616 SPUtils::LoadCmd(chmodCmd, cmdResult);
617 file.close();
618 } else {
619 LOGE("Failed to creat file");
620 return;
621 }
622 } else {
623 std::ofstream file(fileName_, std::ios::trunc);
624 if (!file) {
625 LOGE("Unable to open file");
626 return;
627 }
628 file.close();
629 LOGD("CreatPath already exists: %s", fileName_.c_str());
630 }
631 }
632
GetArgumentParser()633 ArgumentParser& TaskManager::GetArgumentParser()
634 {
635 return parameter_;
636 }
637 }