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 <sstream> 16 #include <fstream> 17 #include <climits> 18 #include <cstring> 19 #include <cstdio> 20 #include <algorithm> 21 #include <iostream> 22 #include <thread> 23 #include <unistd.h> 24 #include <string> 25 #include <regex> 26 #include <cstdarg> 27 #include <sys/time.h> 28 #include <sys/select.h> 29 #include <netinet/in.h> 30 #include "include/sp_utils.h" 31 #include "include/startup_delay.h" 32 #include "include/sp_log.h" 33 #include "include/sdk_data_recv.h" 34 #include "memory_collector.h" 35 #include "collect_result.h" 36 #include "include/sp_task.h" 37 #include "include/sp_utils.h" 38 #include "securec.h" 39 namespace OHOS { 40 namespace SmartPerf { SdkDataRecv()41 SdkDataRecv::SdkDataRecv() 42 { 43 FD_ZERO(&readFds); 44 } 45 ItemData()46 std::map<std::string, std::string> SdkDataRecv::ItemData() 47 { 48 std::map<std::string, std::string> ret; 49 GetSdkDataRealtimeData(ret); 50 LOGI("SdkDataRecv:ItemData map size(%u)", ret.size()); 51 return ret; 52 } 53 StartExecutionOnce(bool isPause)54 void SdkDataRecv::StartExecutionOnce(bool isPause) 55 { 56 if (!isPause) { 57 sdkParams.startTime = SPUtils::GetCurTime(); 58 } 59 OHOS::system::SetParameter("debug.smartperf.sdkdataenable", "1"); 60 collectRunring = true; 61 WLOGI("Starting sdkdata collection in new thread"); 62 if (th_.joinable()) { 63 th_.join(); 64 } 65 th_ = std::thread([this]() { ServerThread(); }); 66 } 67 FinishtExecutionOnce(bool isPause)68 void SdkDataRecv::FinishtExecutionOnce(bool isPause) 69 { 70 WLOGI("Disabling sdk data collection and resetting parameters"); 71 OHOS::system::SetParameter("debug.smartperf.sdkdataenable", "0"); 72 collectRunring = false; 73 if (listenFd != -1) { 74 LOGD("Closing sdk data listenFd: %d", listenFd); 75 close(listenFd); 76 listenFd = -1; 77 } 78 79 if (th_.joinable()) { 80 th_.join(); 81 } 82 if (isPause) { 83 return; 84 } 85 86 if (sdkVec_.empty()) { 87 WLOGI("SDK data is not enabled or sdkvec is empty"); 88 return; 89 } 90 91 LOGD("SDK data stop"); 92 char outSdkDataDirChar[PATH_MAX] = {0x00}; 93 if (realpath(filePath_.c_str(), outSdkDataDirChar) == nullptr) { 94 WLOGE("data dir %s is nullptr", filePath_.c_str()); 95 return; 96 } 97 const std::string outSdkDataPath = std::string(outSdkDataDirChar) + "/sdk_data.csv"; 98 std::ofstream outFile(outSdkDataPath.c_str(), std::ios::out | std::ios::trunc); 99 if (!outFile.is_open()) { 100 WLOGE("data %s open failed", outSdkDataPath.c_str()); 101 return; 102 } 103 std::string title = "source,timestamp,eventName,enable,value\r"; 104 outFile << title << std::endl; 105 for (const auto &item : sdkVec_) { 106 outFile << item << std::endl; 107 } 108 outFile.close(); 109 WLOGI("SDK data written successfully to %s", outSdkDataPath.c_str()); 110 } 111 CreateOhSocketServer(int basePort)112 int SdkDataRecv::CreateOhSocketServer(int basePort) 113 { 114 int i = 0; 115 int socketFd = 0; 116 struct sockaddr_in address; 117 const int reuse = 1; 118 119 LOGD("Creating socket server on base port: %d", basePort); 120 121 socketFd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); 122 if (socketFd < 0) { 123 LOGE("Failed to create socket. Error: %d", errno); 124 return -1; 125 } 126 setsockopt(socketFd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); 127 128 std::fill_n(reinterpret_cast<char*>(&address), sizeof(address), 0); 129 address.sin_family = AF_INET; 130 address.sin_addr.s_addr = inet_addr("127.0.0.1"); 131 132 for (i = 0; i < SOCKET_PORT_NUM_PER_TYPE; i++) { 133 address.sin_port = htons(basePort + i); 134 if (::bind(socketFd, reinterpret_cast<struct sockaddr *>(&address), sizeof(address)) == 0) { 135 LOGD("Socket bound successfully to port: %d", basePort + i); 136 break; 137 } 138 } 139 140 if (i >= SOCKET_PORT_NUM_PER_TYPE) { 141 LOGE("Failed to bind socket after trying all ports starting from: %d", basePort); 142 return -1; 143 } 144 145 if (listen(socketFd, OH_SOCKET_MAX) < 0) { 146 close(socketFd); 147 LOGE("Failed to listen on socket. Error: %d", errno); 148 return -1; 149 } 150 151 LOGD("Listening on port %d, socket fd: %d", basePort + i, socketFd); 152 return socketFd; 153 } ProcessData(std::string & message,ServerParams & params)154 std::string SdkDataRecv::ProcessData(std::string& message, ServerParams ¶ms) 155 { 156 std::stringstream ss(message); 157 std::string enable; 158 std::string eventName; 159 std::string gameHead; 160 std::string item; 161 std::string realTimestamp; 162 std::string source; 163 std::string systime; 164 std::string smartFpsName; 165 std::string timestamp; 166 std::string value; 167 while (std::getline(ss, item, ',')) { 168 std::stringstream itemSS(item); 169 std::string first; 170 std::string second; 171 std::getline(itemSS, first, ':'); 172 std::getline(itemSS, second, ':'); 173 if (first == "src") { 174 source = second; 175 } else if (first == "para0") { 176 eventName = second; 177 if (second == "SmartFps") { 178 smartFpsName = second; 179 } 180 } else if (first == "time") { 181 realTimestamp = std::to_string(SPUtilesTye::StringToSometype<long long>(second) - 182 sdkParams.startTime); 183 timestamp = std::to_string(SPUtilesTye::StringToSometype<long long>(second) - params.startTime); 184 } else if (first == "enable") { 185 enable = second; 186 } else if (first == "value") { 187 value = second; 188 } else if (first == "systime") { 189 systime = second; 190 } 191 } 192 item = source + "," + timestamp + "," + eventName + "," + enable + "," + value + "\r\n"; 193 gameHead = source + "," + timestamp + "," + smartFpsName + "," + enable + 194 "," + value + "," + systime + "\r\n"; 195 GetSdkFpsAndSystime(gameHead); 196 std::unique_lock<std::mutex> lock(realtimeDataLock); 197 sdkDataRealtimeData += source + "_" + realTimestamp + "_" + eventName + "_" + enable + "_" + value + ";"; 198 return item; 199 } 200 OhDataReceive(int index,ServerParams & params)201 std::string SdkDataRecv::OhDataReceive(int index, ServerParams ¶ms) 202 { 203 char receiveBuf[MSG_MAX_LEN]; 204 std::string resStr; 205 int readLen = 0; 206 if ((readLen = read(params.receiveFd[index], receiveBuf, MSG_MAX_LEN)) <= 0) { 207 close(params.receiveFd[index]); 208 params.receiveFd[index] = -1; 209 LOGE("Failed to read data from socket fd[%d]. Read length: %d, Error: %d", index, readLen, errno); 210 return ""; 211 } 212 if (readLen < MSG_MAX_LEN) { 213 receiveBuf[readLen] = '\0'; 214 } else { 215 receiveBuf[MSG_MAX_LEN - 1] = '\0'; 216 } 217 receiveBuffer = receiveBuf; 218 SocketCommandVerification(resStr, params); 219 220 if (!resStr.empty() && resStr.back() == '\n') { 221 resStr.pop_back(); 222 } 223 return resStr; 224 } 225 ServerThread()226 void SdkDataRecv::ServerThread() 227 { 228 for (int i = 0; i < OH_SOCKET_MAX; i++) { 229 sdkParams.receiveFd[i] = -1; 230 } 231 sdkParams.startTime = SPUtils::GetCurTime(); 232 sdkParams.serverFd = CreateOhSocketServer(OH_DATA_PORT); 233 if (sdkParams.serverFd < 0) { 234 LOGE("Failed to create sdk data server, exiting..."); 235 return; 236 } 237 238 if (pipe(sdkParams.pipFd) == -1) { 239 LOGE("Failed to create sdk data pipe."); 240 close(sdkParams.serverFd); 241 return; 242 } 243 listenFd = sdkParams.pipFd[1]; 244 RunServerThread(sdkParams); 245 LOGD("Sdk Data server thread exit."); 246 } 247 RunServerThread(ServerParams & params)248 void SdkDataRecv::RunServerThread(ServerParams ¶ms) 249 { 250 while (collectRunring) { 251 SetUpFdSet(params); 252 if (select(maxFd + 1, &readFds, nullptr, nullptr, nullptr) <= 0) { 253 continue; 254 } 255 for (int i = 0; i < OH_SOCKET_MAX; i++) { 256 HandleReceiveFd(i, params); 257 } 258 HandleServerFd(params); 259 } 260 CleanUpResources(params); 261 } 262 SetUpFdSet(ServerParams & params)263 void SdkDataRecv::SetUpFdSet(ServerParams ¶ms) 264 { 265 FD_ZERO(&readFds); 266 FD_SET(params.serverFd, &readFds); 267 FD_SET(params.pipFd[0], &readFds); 268 269 maxFd = std::max(params.serverFd, params.pipFd[0]); 270 for (int i = 0; i < OH_SOCKET_MAX; i++) { 271 if (params.receiveFd[i] >= 0) { 272 FD_SET(params.receiveFd[i], &readFds); 273 maxFd = std::max(maxFd, params.receiveFd[i]); 274 LOGD("Sdk data adding receiveFd[%d]: %d to FD set", i, params.receiveFd[i]); 275 } 276 } 277 } 278 HandleReceiveFd(int i,ServerParams & params)279 void SdkDataRecv::HandleReceiveFd(int i, ServerParams ¶ms) 280 { 281 if (params.receiveFd[i] >= 0 && FD_ISSET(params.receiveFd[i], &readFds)) { 282 std::string data = OhDataReceive(i, params); 283 if (SPTask::GetInstance().GetRecordState()) { 284 sdkVec_.push_back(data); 285 } 286 } 287 } 288 HandleServerFd(ServerParams & params)289 void SdkDataRecv::HandleServerFd(ServerParams ¶ms) 290 { 291 if (!FD_ISSET(params.serverFd, &readFds)) { 292 return; 293 } 294 295 int fd = accept(params.serverFd, nullptr, nullptr); 296 if (fd < 0) { 297 return; 298 } 299 300 for (int i = 0; i < OH_SOCKET_MAX; i++) { 301 if (params.receiveFd[i] < 0) { 302 params.receiveFd[i] = fd; 303 if (fd > maxFd) { 304 maxFd = fd; 305 } 306 break; 307 } 308 } 309 } 310 CleanUpResources(ServerParams & params)311 void SdkDataRecv::CleanUpResources(ServerParams ¶ms) 312 { 313 if (params.serverFd != -1) { 314 LOGD("Closing sdk data server socket fd: %d", params.serverFd); 315 close(params.serverFd); 316 params.serverFd = -1; 317 } 318 if (params.pipFd[0] != -1) { 319 close(params.pipFd[0]); 320 params.pipFd[0] = -1; 321 } 322 for (int i = 0; i < OH_SOCKET_MAX; i++) { 323 if (params.receiveFd[i] != -1) { 324 close(params.receiveFd[i]); 325 params.receiveFd[i] = -1; 326 } 327 } 328 } 329 GetSdkDataRealtimeData(std::map<std::string,std::string> & dataMap)330 void SdkDataRecv::GetSdkDataRealtimeData(std::map<std::string, std::string> &dataMap) 331 { 332 std::unique_lock<std::mutex> lock(realtimeDataLock); 333 if (sdkDataRealtimeData.size() > 0) { 334 dataMap.insert({"sdkData", sdkDataRealtimeData}); 335 sdkDataRealtimeData.clear(); 336 } 337 } 338 SocketCommandVerification(std::string & resStr,ServerParams & params)339 void SdkDataRecv::SocketCommandVerification(std::string &resStr, ServerParams ¶ms) 340 { 341 bool processFlag = true; 342 while (processFlag) { 343 size_t start = receiveBuffer.find('{'); 344 if (start == std::string::npos) { 345 processFlag = false; 346 break; 347 } 348 349 size_t end = receiveBuffer.find('}', start); 350 if (end == std::string::npos) { 351 processFlag = false; 352 break; 353 } 354 355 std::size_t startPosition = start + 1; 356 std::size_t length = end > start ? end - start - 1 : 0; 357 if (startPosition >= receiveBuffer.size() || length > receiveBuffer.size() - startPosition) { 358 processFlag = false; 359 break; 360 } 361 362 std::string message = receiveBuffer.substr(startPosition, length); 363 resStr += ProcessData(message, params); 364 365 receiveBuffer.erase(0, end + 1); 366 const int bufferSizeCheck = 2; 367 if (receiveBuffer.size() <= bufferSizeCheck) { 368 processFlag = false; 369 } 370 } 371 } 372 GetSdkFpsAndSystime(const std::string & sdkStr)373 void SdkDataRecv::GetSdkFpsAndSystime(const std::string &sdkStr) 374 { 375 std::stringstream ss(sdkStr); 376 std::string filed; 377 std::string curvalue; 378 std::string cursystem; 379 int indexFour = 4; 380 int indexFive = 5; 381 std::vector<std::string> vec; 382 size_t endPos = sdkStr.find("\r\n"); 383 if (endPos != std::string::npos) { 384 ss = std::stringstream(sdkStr.substr(0, endPos)); 385 } 386 387 while (getline(ss, filed, ',')) { 388 vec.push_back(filed); 389 } 390 for (size_t i = 0; i < vec.size(); i++) { 391 curvalue = vec[indexFour]; 392 cursystem = vec[indexFive]; 393 if (curvalue != "0" && cursystem != "0") { 394 sdkFps = curvalue; 395 sdkSystime = cursystem; 396 } 397 } 398 } 399 GetSdkFps() const400 std::string SdkDataRecv::GetSdkFps() const 401 { 402 return sdkFps; 403 } 404 GetSdkSystime() const405 std::string SdkDataRecv::GetSdkSystime() const 406 { 407 return sdkSystime; 408 } 409 SetFilePath(const std::string & path)410 void SdkDataRecv::SetFilePath(const std::string& path) 411 { 412 filePath_ = path; 413 } 414 } 415 }