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 #include <iostream>
16 #include <thread>
17 #include <string>
18 #include <climits>
19 #include "include/sp_profiler_factory.h"
20 #include "include/sp_utils.h"
21 #include "include/FPS.h"
22 #include "include/FileDescriptor.h"
23 #include "include/RAM.h"
24 #include "include/CPU.h"
25 #include "include/Capture.h"
26 #include "include/startup_delay.h"
27 #include "include/sp_log.h"
28 #include "ByTrace.h"
29 #include <cstdio>
30 #include <ios>
31 #include <vector>
32 #include <fstream>
33 #include <sstream>
34 #include <regex>
35 #include "unistd.h"
36 #include <future>
37 #include "include/common.h"
38 #include "include/sp_csv_util.h"
39 #include "include/sp_thread_socket.h"
40 #include "effective.h"
41 namespace OHOS {
42 namespace SmartPerf {
43 const long long RM_1000 = 1000;
44 const long long END_WAITING_TIME = 8; // End waiting time,unit seconds
ParseCommandArgs(std::string & command)45 std::vector<std::string> ParseCommandArgs(std::string &command)
46 {
47 std::vector<std::string> args;
48 size_t pos = 0;
49 while ((pos = command.find(" ")) != std::string::npos) {
50 args.push_back(command.substr(0, pos));
51 command.erase(0, pos + 1);
52 }
53 args.push_back(command);
54 return args;
55 }
56
57 // init::-SESSIONID 12345678 -INTERVAL 1000 -PKG ohos.samples.ecg -c -g -t -p -f -r -fl 30
ParseToTask(std::string command,TaskInfo & taskInfo)58 static ExceptionMsg ParseToTask(std::string command, TaskInfo &taskInfo)
59 {
60 StuckNotification snf;
61 snf.isEffective = false;
62 std::string sessionId;
63 long long interval = 1000;
64 std::string pkg;
65 std::string pid;
66 bool isFPS = false;
67 bool isPrint = false;
68 std::vector<std::string> configs;
69 std::vector<std::string> args = ParseCommandArgs(command);
70 for (size_t i = 0; i < args.size(); i++) {
71 if (args[i] == COMMAND_MAP_REVERSE.at(CommandType::CT_SESSIONID)) {
72 sessionId = args[++i];
73 } else if (args[i] == COMMAND_MAP_REVERSE.at(CommandType::CT_INTERVAL)) {
74 interval = SPUtilesTye::StringToSometype<long long>(args[++i]);
75 } else if (args[i] == COMMAND_MAP_REVERSE.at(CommandType::CT_PKG)) {
76 pkg = args[++i];
77 } else if (args[i] == COMMAND_MAP_REVERSE.at(CommandType::CT_PID)) {
78 pid = args[++i];
79 } else if (args[i] == COMMAND_MAP_REVERSE.at(CommandType::CT_PRINT)) {
80 isPrint = true;
81 } else if (args[i] == COMMAND_MAP_REVERSE.at(CommandType::CT_FL)) { // 获取用户fps的值,并赋给snf. CT_FL
82 snf.fps = SPUtilesTye::StringToSometype<int>(args[++i]);
83 snf.isEffective = true;
84 } else if (args[i] == COMMAND_MAP_REVERSE.at(CommandType::CT_FTL)) { // 获取frameTime的值 CT_FTL
85 snf.frameTime = SPUtilesTye::StringToSometype<int>(args[++i]);
86 snf.isEffective = true;
87 } else {
88 if (args[i] == COMMAND_MAP_REVERSE.at(CommandType::CT_F)) { // 判断用户设置是否有-f
89 isFPS = true;
90 }
91 if (COMMAND_MAP.end() != COMMAND_MAP.find(args[i])) {
92 configs.push_back(args[i]);
93 }
94 }
95 }
96 if (snf.isEffective && (!isFPS)) {
97 return ExceptionMsg::TASK_CONFIG_NULL;
98 }
99 if (sessionId.empty()) {
100 LOGE("ExceptionMsg ParseToTask sessoin id is null");
101 return ExceptionMsg::SESSION_ID_NULL;
102 } else if (configs.size() == 0) {
103 LOGE("ExceptionMsg ParseToTask configs size is 0");
104 return ExceptionMsg::TASK_CONFIG_NULL;
105 }
106 taskInfo = { sessionId, pkg, pid, configs, interval, snf, isPrint };
107 return ExceptionMsg::NO_ERR;
108 }
109
InitTask(const std::string & recvStr)110 ErrCode SPTask::InitTask(const std::string &recvStr)
111 {
112 std::string result = "";
113 const std::string hiprofiler = CMD_COMMAND_MAP.at(CmdCommand::HIPROFILER);
114 SPUtils::LoadCmd(hiprofiler, result);
115 result.clear();
116 const std::string perf = CMD_COMMAND_MAP.at(CmdCommand::PERF);
117 SPUtils::LoadCmd(perf, result);
118 std::cout << recvStr.substr(recvStr.find("-SESSIONID")) << std::endl;
119 WLOGI("Received init task string: %s", recvStr.substr(recvStr.find("-SESSIONID")).c_str());
120 ExceptionMsg exMsg = ParseToTask(recvStr, curTaskInfo);
121 if (exMsg == ExceptionMsg::NO_ERR) {
122 if (taskMgr_ != nullptr) {
123 taskMgr_->Stop();
124 taskMgr_->WriteToCSV();
125 }
126 taskMgr_ = std::make_shared<TaskManager>(true);
127 taskMgr_->AddTask(recvStr);
128 if (curTaskInfo.stuckInfo.isEffective) {
129 if (curTaskInfo.stuckInfo.fps != 0) {
130 Effective::GetInstance().fps_ = curTaskInfo.stuckInfo.fps;
131 } else {
132 Effective::GetInstance().frameTime_ = curTaskInfo.stuckInfo.frameTime;
133 }
134 }
135 SetAppInitFlag();
136 LOGD("InitTask success, task initialized.");
137 return ErrCode::OK;
138 }
139
140 std::string errInfo = EXCEPTION_MSG_MAP.at(exMsg);
141 LOGE("InitTask error(%s)", errInfo.c_str());
142 return ErrCode::FAILED;
143 }
144
InitDataFile()145 void SPTask::InitDataFile()
146 {
147 gpuCounter.GetGpuCounterSaveReportData().clear();
148 startTime = SPUtils::GetCurTime();
149 std::vector<std::string> files = {
150 "sdk_data.csv",
151 "gpu_counter.csv",
152 "t_general_info.csv",
153 "t_index_info.csv",
154 };
155 std::string fileDir = baseOutPath + "/" + curTaskInfo.sessionId;
156
157 for (const auto &file: files) {
158 std::string filePath = fileDir + "/" + file;
159 char filePathChar[PATH_MAX] = {0x00};
160 if (realpath(filePath.c_str(), filePathChar) == nullptr) {
161 LOGE("%s is not exist, init finish.", filePath.c_str());
162 continue;
163 }
164 std::remove(filePathChar);
165 }
166
167 LOGD("Data file initialization completed.");
168 return;
169 }
170
SetProfilerPid()171 void SPTask::SetProfilerPid()
172 {
173 SpProfilerFactory::editorFlag = true;
174 std::string processId = "";
175 std::string processIds = "";
176 OHOS::SmartPerf::StartUpDelay sp;
177 processId = sp.GetPidByPkg(curTaskInfo.packageName, &processIds);
178 SpProfilerFactory::SetProfilerPidByPkg(processId, processIds);
179 }
180
StartTask(std::function<void (const std::string &)> msgTask)181 ErrCode SPTask::StartTask(std::function<void(const std::string&)> msgTask)
182 {
183 LOGD("Task starting...");
184 RAM &ram = RAM::GetInstance();
185 ram.SetFirstFlag();
186 LOGD("RAM first flag set.");
187 if (!isInit) {
188 WLOGE("Initialization failed.");
189 return ErrCode::FAILED;
190 }
191 isRunning = true;
192 LOGD("Task initialized, realTimeStart = %lld", SPUtils::GetCurTime());
193 InitDataFile();
194 LOGD("Data files initialized.");
195 thread = std::thread([=]() {
196 if (taskMgr_ == nullptr) {
197 return;
198 }
199 WLOGI("Starting data collection thread.");
200 std::string thisBasePath = baseOutPath + "/" + curTaskInfo.sessionId;
201 CreatPath(thisBasePath);
202 std::string outIndexpath = thisBasePath + "/t_index_info.csv";
203 taskMgr_->SetFilePath(outIndexpath);
204 taskMgr_->SetIPCCallback(msgTask);
205 taskMgr_->EnableIPCCallback();
206 taskMgr_->SetNextTime(nextTime);
207 taskMgr_->Start(false);
208 taskMgr_->Wait();
209 });
210 EnablePrint();
211 return ErrCode::OK;
212 }
213
CreatPath(const std::string & path)214 void SPTask::CreatPath(const std::string& path)
215 {
216 if (!SPUtils::FileAccess(path)) {
217 LOGD("CreatPath does not exist, attempting to create: %s", path.c_str());
218 std::string cmdResult;
219 std::string creatPath = CMD_COMMAND_MAP.at(CmdCommand::CREAT_DIR) + path;
220 bool cmdSuccess = SPUtils::LoadCmd(creatPath, cmdResult);
221 if (cmdSuccess) {
222 LOGD("CreatPath created successfully: %s", path.c_str());
223 } else {
224 LOGE("Failed to create path: %s. Command result: %s", path.c_str(), cmdResult.c_str());
225 }
226 } else {
227 std::ofstream file(path, std::ios::trunc);
228 if (!file) {
229 LOGE("Unable to open file");
230 return;
231 }
232 file.close();
233 LOGD("CreatPath already exists: %s", path.c_str());
234 }
235 }
236
SetTaskInfo()237 std::map<std::string, std::string> SPTask::SetTaskInfo()
238 {
239 long long endTime = SPUtils::GetCurTime();
240 long long testDuration = (endTime - startTime) / 1000;
241 std::string refreshrate;
242 LOGD("Test duration: %lld seconds", testDuration);
243 const std::string gpuDataVersion = "1.1";
244 std::string screenStr = SPUtils::GetScreen();
245 size_t pos3 = screenStr.find("=");
246 if (pos3 != std::string::npos) {
247 refreshrate = screenStr.substr(pos3 + 1);
248 LOGD("Screen refresh rate: %s", refreshrate.c_str());
249 } else {
250 LOGW("Failed to extract refresh rate from screen string: %s", screenStr.c_str());
251 }
252
253 std::map<std::string, std::string> taskInfoMap = {
254 { "sessionId", curTaskInfo.sessionId },
255 { "taskId", curTaskInfo.sessionId },
256 { "appName", curTaskInfo.packageName },
257 { "packageName", curTaskInfo.packageName },
258 { "pid", curTaskInfo.pid },
259 { "startTime", std::to_string(startTime) },
260 { "endTime", std::to_string(endTime) },
261 { "testDuration", std::to_string(testDuration) },
262 { "taskName", "testtask" },
263 { "board", SPUtils::GetProductName() },
264 { "target_fps", refreshrate },
265 { "gpuDataVersion", gpuDataVersion },
266 { "battery_change", std::to_string(battaryEnd - battaryStart) },
267 };
268 return taskInfoMap;
269 }
270
StopGetInfo()271 void SPTask::StopGetInfo()
272 {
273 bool isTcpMessage = true;
274
275 std::map<std::string, std::string> taskInfoMap = SetTaskInfo();
276 std::map<std::string, std::string> deviceInfo = SPUtils::GetDeviceInfo();
277 if (deviceInfo.empty()) {
278 LOGW("Failed to get device info when stop.");
279 }
280 std::map<std::string, std::string> cpuInfo = SPUtils::GetCpuInfo(isTcpMessage);
281 if (cpuInfo.empty()) {
282 LOGW("Failed to get CPU info when stop.");
283 }
284 std::map<std::string, std::string> gpuInfo = SPUtils::GetGpuInfo(isTcpMessage);
285 if (gpuInfo.empty()) {
286 LOGW("Failed to get GPU info when stop.");
287 }
288 std::map<std::string, std::string> destMap;
289 destMap.merge(taskInfoMap);
290 destMap.merge(deviceInfo);
291 destMap.merge(cpuInfo);
292 destMap.merge(gpuInfo);
293 OHOS::SmartPerf::SpCsvUtil::WriteCsvH(destMap);
294 WLOGI("Write CSV header done.");
295 }
296
StopTask()297 ErrCode SPTask::StopTask()
298 {
299 if (taskMgr_ != nullptr) {
300 taskMgr_->Stop();
301 taskMgr_->WriteToCSV();
302 }
303 if (GetRecordState()) {
304 WLOGI("Record state is valid. Stopping task and cleaning up.");
305 StopGetInfo();
306 recordState = false;
307 } else {
308 WLOGW("Record state is invalid. Skipping task stop operations.");
309 }
310
311 WLOGI("Stopping task. isRunning: %d, isInit: %d", isRunning, isInit);
312 isRunning = false;
313 isInit = false;
314 if (stdOutLog >= 0) {
315 close(stdOutLog);
316 stdOutLog = -1;
317 }
318 if (thread.joinable()) {
319 LOGD("Joining thread.");
320 thread.join();
321 }
322 LOGD("Killing Hiperf command.");
323 WLOGI("Task successfully stopped.");
324 return ErrCode::OK;
325 }
326
CheckTcpParam(const std::string & str,std::string & errorInfo)327 bool SPTask::CheckTcpParam(const std::string& str, std::string &errorInfo)
328 {
329 std::set<std::string> keys;
330 std::string params;
331 if (str.find("-SESSIONID") != std::string::npos) {
332 params = str.substr(str.find("-SESSIONID"));
333 } else {
334 LOGE("Init parameter error not contain '-SESSIONID'");
335 return false;
336 }
337 LOGD("Start validating Init parameter: %s", params.c_str());
338
339 for (auto& a : COMMAND_MAP) {
340 keys.insert(a.first.substr(1)); // 不需要前面的'-'
341 }
342 bool isValid = SPUtils::VeriyParameter(keys, params, errorInfo);
343 if (isValid) {
344 LOGD("Init parameter validation successful");
345 } else {
346 LOGE("Init parameter validation failed, Error: %s", errorInfo.c_str());
347 }
348 return isValid;
349 }
350
KillHiperfCmd()351 void SPTask::KillHiperfCmd()
352 {
353 long long now = 0;
354 long long runTime = 0;
355 const std::string killCmd = CMD_COMMAND_MAP.at(CmdCommand::KILL_CMD) + "-9 ";
356 std::string result;
357 std::vector<std::string> out;
358
359 if (startCaptuerTime <= 0) {
360 return;
361 }
362
363 now = SPUtils::GetCurTime();
364 runTime = now > startCaptuerTime ? now - startCaptuerTime : LLONG_MAX - startCaptuerTime + now;
365 runTime = runTime / RM_1000; // Convert to seconds
366
367 LOGD("Preparing to exit run time(%lld)", runTime);
368 do {
369 out.clear();
370 const std::string hiprofilerPid = CMD_COMMAND_MAP.at(CmdCommand::HIPROFILER_PID);
371 SPUtils::LoadCmd(hiprofilerPid, result);
372 SPUtils::StrSplit(result, " ", out);
373 if (out.empty()) {
374 break;
375 }
376
377 sleep(1);
378 } while (END_WAITING_TIME - runTime++ > 0);
379
380 out.clear();
381 const std::string hiprofilerPid = CMD_COMMAND_MAP.at(CmdCommand::HIPROFILER_PID);
382 SPUtils::LoadCmd(hiprofilerPid, result);
383 SPUtils::StrSplit(result, " ", out);
384 LOGD("pidof hiprofiler_cmd size(%d)", out.size());
385 for (auto it = out.begin(); out.end() != it; ++it) {
386 result.clear();
387 SPUtils::LoadCmd(killCmd + (*it), result);
388 }
389
390 return;
391 }
392
GetRecordState()393 bool SPTask::GetRecordState()
394 {
395 return recordState;
396 }
GetCurrentBattary()397 int SPTask::GetCurrentBattary()
398 {
399 std::string content;
400 const std::string cmd = "hidumper -s 3302 -a -i | grep capacity";
401 SPUtils::LoadCmd(cmd, content);
402 content = content.substr(content.find(':') + 1);
403 if (content == "") {
404 WLOGE("Battery capacity is empty.");
405 return 0;
406 }
407 return SPUtilesTye::StringToSometype<int>(content);
408 }
409
StartRecord()410 ErrCode SPTask::StartRecord()
411 {
412 battaryStart = GetCurrentBattary();
413 startTime = SPUtils::GetCurTime();
414 WLOGI("StartRecord initiated: Battery %d, Start time %lld.", battaryStart, startTime);
415 while (startTime > nextTime) {
416 std::this_thread::sleep_for(std::chrono::milliseconds(1));
417 }
418 SetStartFlag();
419 if (taskMgr_ != nullptr) {
420 taskMgr_->SetRecordState(true);
421 }
422 return ErrCode::OK;
423 }
424
SetStartFlag()425 void SPTask::SetStartFlag()
426 {
427 InitDataFile();
428 recordState = true;
429 }
430
StopRecord()431 ErrCode SPTask::StopRecord()
432 {
433 battaryEnd = GetCurrentBattary();
434 long long stopRecordTime = SPUtils::GetCurTime();
435 WLOGI("StopRecord initiated: Battery %d, Stop time %lld.", battaryEnd, stopRecordTime);
436 while (stopRecordTime > nextTime) {
437 std::this_thread::sleep_for(std::chrono::milliseconds(1));
438 }
439 ClearStopFlag();
440 if (taskMgr_ != nullptr) {
441 taskMgr_->SetRecordState(false);
442 }
443 return ErrCode::OK;
444 }
445
ClearStopFlag()446 void SPTask::ClearStopFlag()
447 {
448 recordState = false;
449 std::string outGpuCounterDataPath = baseOutPath + "/" + curTaskInfo.sessionId;
450
451 if (isInit) {
452 StopGetInfo();
453 gpuCounter.GetInstance().SaveData(outGpuCounterDataPath);
454 }
455
456 Capture::GetInstance().SetCollectionNum();
457 KillHiperfCmd();
458 }
459
GetCsvTitle()460 std::string SPTask::GetCsvTitle()
461 {
462 return csvTitle;
463 }
SetRecordState(bool status)464 void SPTask::SetRecordState(bool status)
465 {
466 recordState = status;
467 }
EnablePrint()468 void SPTask::EnablePrint()
469 {
470 if (curTaskInfo.isPrint) {
471 stdOutLog = SPUtils::GetTtyDeviceFd();
472 if (dup2(stdOutLog, STDERR_FILENO) < 0) {
473 LOGE("Dup2 fail");
474 } else {
475 close(stdOutLog);
476 stdOutLog = -1;
477 }
478 } else {
479 int &fd = SPUtils::GetTtyDeviceFd();
480 if (fd >= 0) {
481 close(fd);
482 fd = -1;
483 }
484 }
485 }
486
SetAppCmd(const std::string & recvBuf)487 void SPTask::SetAppCmd(const std::string &recvBuf)
488 {
489 std::stringstream ssLine(recvBuf);
490 std::string word = "";
491 int count = 0;
492 int counter = 3;
493 while (ssLine >> word) {
494 count++;
495 if (count == counter) {
496 curTaskInfo.packageName = word;
497 } else if (count > counter) {
498 if (word.find(":::") != std::string::npos) {
499 size_t pos = word.find(":::");
500 word = word.substr(0, pos);
501 }
502 curTaskInfo.taskConfig.push_back(word);
503 }
504 }
505 }
506
GetCurTaskInfo()507 TaskInfo SPTask::GetCurTaskInfo()
508 {
509 return curTaskInfo;
510 }
511
SetAppInitFlag()512 void SPTask::SetAppInitFlag()
513 {
514 isInit = true;
515 if (!GetCurTaskInfo().packageName.empty()) {
516 SetProfilerPid();
517 SpProfilerFactory::SetProfilerPkg(curTaskInfo.packageName);
518 }
519 }
520 }
521 }
522