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