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 package ohos.devtools.datasources.transport.grpc; 17 18 import com.google.protobuf.ByteString; 19 import com.google.protobuf.InvalidProtocolBufferException; 20 import io.grpc.ManagedChannel; 21 import io.grpc.Status; 22 import io.grpc.StatusRuntimeException; 23 import ohos.devtools.datasources.transport.grpc.service.CommonTypes; 24 import ohos.devtools.datasources.transport.grpc.service.ProcessPluginConfig; 25 import ohos.devtools.datasources.transport.grpc.service.ProcessPluginResult; 26 import ohos.devtools.datasources.transport.grpc.service.ProfilerServiceTypes; 27 import ohos.devtools.datasources.utils.common.util.CommonUtil; 28 import ohos.devtools.datasources.utils.common.util.DateTimeUtil; 29 import ohos.devtools.datasources.utils.device.entity.DeviceType; 30 import ohos.devtools.datasources.utils.process.entity.ProcessInfo; 31 import ohos.devtools.datasources.utils.profilerlog.ProfilerLogManager; 32 import ohos.devtools.datasources.utils.session.service.SessionManager; 33 import ohos.devtools.views.common.LayoutConstants; 34 import org.apache.commons.codec.digest.DigestUtils; 35 import org.apache.logging.log4j.LogManager; 36 import org.apache.logging.log4j.Logger; 37 38 import java.io.File; 39 import java.io.FileInputStream; 40 import java.io.IOException; 41 import java.net.InetAddress; 42 import java.util.ArrayList; 43 import java.util.Iterator; 44 import java.util.List; 45 import java.util.Objects; 46 import java.util.concurrent.ConcurrentHashMap; 47 48 import static io.grpc.Status.DEADLINE_EXCEEDED; 49 import static io.grpc.Status.INTERNAL; 50 import static io.grpc.Status.UNAVAILABLE; 51 import static ohos.devtools.datasources.utils.common.Constant.DEVTOOLS_PLUGINS_FULL_PATH; 52 import static ohos.devtools.datasources.utils.common.Constant.PROCESS_PLUGS; 53 import static ohos.devtools.datasources.utils.device.entity.DeviceType.LEAN_HOS_DEVICE; 54 import static ohos.devtools.views.common.Constant.IS_SUPPORT_NEW_HDC; 55 56 /** 57 * Provide device-side grpc interface encapsulation for each module in the application 58 * 59 * @since 2021/11/22 60 */ 61 public final class HiProfilerClient { 62 private static final Logger LOGGER = LogManager.getLogger(HiProfilerClient.class); 63 64 /** 65 * Singleton Class Instance 66 */ 67 private static final HiProfilerClient INSTANCE = new HiProfilerClient(); 68 69 private static final String IP = InetAddress.getLoopbackAddress().getHostAddress(); 70 71 private static final int RETRY_COUNT = 2; 72 73 /** 74 * Used to store the created Profiler 75 */ 76 private static ConcurrentHashMap<String, ProfilerClient> profilerClientMap = 77 new ConcurrentHashMap<>(CommonUtil.collectionSize(0)); 78 HiProfilerClient()79 private HiProfilerClient() { 80 } 81 82 /** 83 * Get instance 84 * 85 * @return HiProfilerClient 86 */ getInstance()87 public static HiProfilerClient getInstance() { 88 if (ProfilerLogManager.isInfoEnabled()) { 89 LOGGER.info("getInstance"); 90 } 91 return INSTANCE; 92 } 93 94 /** 95 * Get profilerclient 96 * 97 * @param ip ip address 98 * @param port port number 99 * @param channel channel 100 * @return ProfilerClient 101 */ getProfilerClient(String ip, int port, ManagedChannel channel)102 public ProfilerClient getProfilerClient(String ip, int port, ManagedChannel channel) { 103 if (ProfilerLogManager.isInfoEnabled()) { 104 LOGGER.info("getProfilerClient"); 105 } 106 String mapKey = IP + port; 107 if (port <= 0 || port > LayoutConstants.PORT) { 108 return null; 109 } 110 if (Objects.isNull(profilerClientMap.get(mapKey))) { 111 ProfilerClient profilerClient = null; 112 profilerClient = new ProfilerClient(IP, port, channel); 113 profilerClientMap.put(mapKey, profilerClient); 114 return profilerClient; 115 } 116 return profilerClientMap.get(mapKey); 117 } 118 119 /** 120 * get profilerClient. 121 * 122 * @param ip ip address 123 * @param port port number 124 * @return ProfilerClient 125 */ getProfilerClient(String ip, int port)126 public ProfilerClient getProfilerClient(String ip, int port) { 127 if (ProfilerLogManager.isInfoEnabled()) { 128 LOGGER.info("getProfilerClient"); 129 } 130 if (port <= 0 || port > LayoutConstants.PORT) { 131 return null; 132 } 133 String mapKey = IP + port; 134 if (profilerClientMap.get(mapKey) == null) { 135 ProfilerClient profilerClient = new ProfilerClient(IP, port); 136 profilerClientMap.put(mapKey, profilerClient); 137 return profilerClient; 138 } 139 return profilerClientMap.get(mapKey); 140 } 141 142 /** 143 * Destroy profilerClient 144 * 145 * @param ip ip address 146 * @param port port number 147 * @return boolean 148 */ destroyProfiler(String ip, int port)149 public boolean destroyProfiler(String ip, int port) { 150 if (ProfilerLogManager.isInfoEnabled()) { 151 LOGGER.info("destroyProfiler"); 152 } 153 if (port <= 0 || port > LayoutConstants.PORT) { 154 return false; 155 } 156 String mapKey = IP + port; 157 if (Objects.isNull(profilerClientMap.get(mapKey))) { 158 return true; 159 } 160 ProfilerClient client = profilerClientMap.get(mapKey); 161 client.shutdown(); 162 return profilerClientMap.remove(mapKey, client); 163 } 164 165 /** 166 * requestCreateSession 167 * 168 * @param deviceIp deviceIp 169 * @param port port 170 * @param request request 171 * @return ProfilerServiceTypes.CreateSessionResponse 172 */ requestCreateSession(String deviceIp, int port, ProfilerServiceTypes.CreateSessionRequest request)173 public ProfilerServiceTypes.CreateSessionResponse requestCreateSession(String deviceIp, int port, 174 ProfilerServiceTypes.CreateSessionRequest request) { 175 if (ProfilerLogManager.isInfoEnabled()) { 176 LOGGER.info("requestCreateSession"); 177 } 178 if (port <= 0 || port > LayoutConstants.PORT) { 179 return ProfilerServiceTypes.CreateSessionResponse.newBuilder().setSessionId(-1).build(); 180 } 181 ProfilerClient client = getProfilerClient(deviceIp, port); 182 try { 183 return client.createSession(request); 184 } catch (StatusRuntimeException exception) { 185 handleGrpcInterface(exception, port, client); 186 if (ProfilerLogManager.isErrorEnabled()) { 187 LOGGER.error("StatusRuntimeException ", exception); 188 } 189 return ProfilerServiceTypes.CreateSessionResponse.newBuilder().setSessionId(-1).build(); 190 } 191 } 192 193 /** 194 * createSession for ListProcess 195 * 196 * @param port port number 197 * @param name name 198 * @param pid pid 199 * @param reportProcessTree report process tree 200 * @param deviceType DeviceType 201 * @return int 202 */ processListCreateSession(int port, String name, int pid, boolean reportProcessTree, DeviceType deviceType)203 public int processListCreateSession(int port, String name, int pid, boolean reportProcessTree, 204 DeviceType deviceType) { 205 if (ProfilerLogManager.isInfoEnabled()) { 206 LOGGER.info("processListCreateSession"); 207 } 208 if (port <= 0 || port > LayoutConstants.PORT) { 209 return -1; 210 } 211 ProcessPluginConfig.ProcessConfig plug = 212 ProcessPluginConfig.ProcessConfig.newBuilder().setReportProcessTree(true).build(); 213 ProfilerServiceTypes.ProfilerSessionConfig sessionConfig = ProfilerServiceHelper 214 .profilerSessionConfig(true, null, 10, 215 ProfilerServiceTypes.ProfilerSessionConfig.BufferConfig.Policy.RECYCLE, 5000); 216 String sha256 = ""; 217 CommonTypes.ProfilerPluginConfig plugConfig = 218 ProfilerServiceHelper.profilerPluginConfig(name, sha256, 2, plug.toByteString()); 219 List<CommonTypes.ProfilerPluginConfig> plugs = new ArrayList(); 220 plugs.add(plugConfig); 221 ProfilerServiceTypes.CreateSessionRequest request = 222 ProfilerServiceHelper.createSessionRequest(CommonUtil.getRequestId(), sessionConfig, plugs); 223 ProfilerServiceTypes.CreateSessionResponse response = null; 224 ProfilerClient client = getProfilerClient("", port); 225 try { 226 response = client.createSession(request); 227 LOGGER.info("process Session start444 {} ", DateTimeUtil.getNowTimeLong()); 228 } catch (StatusRuntimeException exception) { 229 handleGrpcInterface(exception, port, client); 230 if (ProfilerLogManager.isErrorEnabled()) { 231 LOGGER.error("processListCreateSession ", exception); 232 } 233 return -1; 234 } 235 return response.getSessionId(); 236 } 237 238 /** 239 * getSha256 240 * 241 * @param pluginFileName pluginFileName 242 * @return String 243 */ getSha256(String pluginFileName)244 public static String getSha256(String pluginFileName) { 245 return ""; 246 } 247 248 /** 249 * getSTDSha256 250 * 251 * @param pluginFileName pluginFileName 252 * @return String 253 */ getSTDSha256(String pluginFileName)254 public static String getSTDSha256(String pluginFileName) { 255 return ""; 256 } 257 258 /** 259 * Request to start session 260 * 261 * @param deviceIp deviceIp 262 * @param port port number 263 * @param sessionId sessionId 264 * @return boolean 265 */ requestStartSession(String deviceIp, int port, int sessionId)266 public boolean requestStartSession(String deviceIp, int port, int sessionId) { 267 if (ProfilerLogManager.isInfoEnabled()) { 268 LOGGER.info("requestStartSession"); 269 } 270 if (port <= 0 || port > LayoutConstants.PORT) { 271 return false; 272 } 273 return requestStartSession(deviceIp, port, sessionId, 0); 274 } 275 requestStartSession(String deviceIp, int port, int sessionId, int retryCount)276 private boolean requestStartSession(String deviceIp, int port, int sessionId, int retryCount) { 277 if (ProfilerLogManager.isInfoEnabled()) { 278 LOGGER.info("requestStartSession"); 279 } 280 ProfilerServiceTypes.StartSessionRequest requestStartSession = 281 ProfilerServiceHelper.startSessionRequest(CommonUtil.getRequestId(), sessionId, new ArrayList<>()); 282 ProfilerServiceTypes.StartSessionResponse response = null; 283 ProfilerClient client = getProfilerClient(deviceIp, port); 284 try { 285 response = client.startSession(requestStartSession); 286 } catch (StatusRuntimeException exception) { 287 if (ProfilerLogManager.isErrorEnabled()) { 288 LOGGER.error("requestStartSession", exception.getMessage()); 289 } 290 handleGrpcInterface(exception, port, client); 291 return false; 292 } 293 return response.getStatus() == 0 ? true : false; 294 } 295 296 /** 297 * requestStopSession 298 * 299 * @param deviceIp deviceIp 300 * @param port port number 301 * @param sessionId sessionId 302 * @param isForce isForce 303 * @return boolean 304 */ requestStopSession(String deviceIp, int port, int sessionId, boolean isForce)305 public boolean requestStopSession(String deviceIp, int port, int sessionId, boolean isForce) { 306 if (ProfilerLogManager.isInfoEnabled()) { 307 LOGGER.info("requestStopSession"); 308 } 309 if (port <= 0 || port > LayoutConstants.PORT) { 310 return false; 311 } 312 return requestStopSession(deviceIp, port, sessionId); 313 } 314 requestStopSession(String deviceIp, int port, int sessionId)315 private boolean requestStopSession(String deviceIp, int port, int sessionId) { 316 if (ProfilerLogManager.isInfoEnabled()) { 317 LOGGER.info("requestStopSession"); 318 } 319 ProfilerClient client = getProfilerClient(deviceIp, port); 320 ProfilerServiceTypes.StopSessionRequest stopSession = 321 ProfilerServiceHelper.stopSessionRequest(CommonUtil.getRequestId(), sessionId); 322 ProfilerServiceTypes.StopSessionResponse response = null; 323 try { 324 response = client.stopSession(stopSession); 325 } catch (StatusRuntimeException exception) { 326 if (ProfilerLogManager.isErrorEnabled()) { 327 LOGGER.info("stopSession has Exception {}", exception.getMessage()); 328 } 329 handleGrpcInterface(exception, port, client); 330 return false; 331 } 332 return response.getStatus() == 0 ? true : false; 333 } 334 335 /** 336 * request destory Session 337 * 338 * @param deviceIp deviceIp 339 * @param port port number 340 * @param sessionId sessionId 341 * @return boolean 342 */ requestDestroySession(String deviceIp, int port, int sessionId)343 public boolean requestDestroySession(String deviceIp, int port, int sessionId) { 344 if (ProfilerLogManager.isInfoEnabled()) { 345 LOGGER.info("requestDestroySession"); 346 } 347 if (port <= 0 || port > LayoutConstants.PORT) { 348 return false; 349 } 350 return requestDestroySession(deviceIp, port, sessionId, 0); 351 } 352 requestDestroySession(String deviceIp, int port, int sessionId, int retryCount)353 private boolean requestDestroySession(String deviceIp, int port, int sessionId, int retryCount) { 354 if (ProfilerLogManager.isInfoEnabled()) { 355 LOGGER.info("requestDestroySession"); 356 } 357 ProfilerClient client = getProfilerClient(deviceIp, port); 358 ProfilerServiceTypes.DestroySessionRequest req = 359 ProfilerServiceHelper.destroySessionRequest(CommonUtil.getRequestId(), sessionId); 360 ProfilerServiceTypes.DestroySessionResponse response = null; 361 try { 362 response = client.destroySession(req); 363 } catch (StatusRuntimeException exception) { 364 if (ProfilerLogManager.isErrorEnabled()) { 365 LOGGER.error("requestDestroySession failed {}", exception.getMessage()); 366 } 367 handleGrpcInterface(exception, port, client); 368 int retryCounts = retryCount + 1; 369 if (retryCounts > RETRY_COUNT) { 370 return true; 371 } 372 return requestDestroySession(deviceIp, port, sessionId, retryCounts); 373 } 374 375 return response.getStatus() == 0 ? true : false; 376 } 377 378 /** 379 * Fetch process data 380 * 381 * @param deviceIp deviceIp 382 * @param port port number 383 * @param sessionId sessionId 384 * @return List <ProcessInfo> 385 */ fetchProcessData(String deviceIp, int port, int sessionId)386 public List<ProcessInfo> fetchProcessData(String deviceIp, int port, int sessionId) { 387 if (ProfilerLogManager.isInfoEnabled()) { 388 LOGGER.info("fetchProcessData"); 389 } 390 ProfilerClient client = getProfilerClient(deviceIp, port); 391 List<ProcessInfo> processInfos = new ArrayList<>(); 392 ProfilerServiceTypes.FetchDataRequest fetchData = 393 ProfilerServiceHelper.fetchDataRequest(CommonUtil.getRequestId(), sessionId, null); 394 Iterator<ProfilerServiceTypes.FetchDataResponse> res = null; 395 try { 396 res = client.fetchData(fetchData); 397 } catch (StatusRuntimeException exception) { 398 if (ProfilerLogManager.isErrorEnabled()) { 399 LOGGER.info("GrpcException {}", exception.getMessage()); 400 } 401 return new ArrayList<>(); 402 } 403 try { 404 if (res.hasNext()) { 405 ProfilerServiceTypes.FetchDataResponse fetchDataResponse = res.next(); 406 int pluginStatus = fetchDataResponse.getStatus(); 407 if (pluginStatus != 0) { 408 return new ArrayList<>(); 409 } 410 List<CommonTypes.ProfilerPluginData> lists = fetchDataResponse.getPluginDataList(); 411 processInfos = extractedData(lists); 412 } 413 } catch (StatusRuntimeException statusRuntimeException) { 414 if (ProfilerLogManager.isErrorEnabled()) { 415 LOGGER.error(" get ProcessInfo failed {}", statusRuntimeException.getMessage()); 416 } 417 } 418 return processInfos; 419 } 420 extractedData(List<CommonTypes.ProfilerPluginData> lists)421 private List<ProcessInfo> extractedData(List<CommonTypes.ProfilerPluginData> lists) { 422 if (ProfilerLogManager.isInfoEnabled()) { 423 LOGGER.info("extractedData"); 424 } 425 List<ProcessInfo> process = new ArrayList<>(); 426 if (lists.isEmpty()) { 427 return process; 428 } 429 CommonTypes.ProfilerPluginData profilerPluginData = lists.get(0); 430 if (PROCESS_PLUGS.equals(profilerPluginData.getName())) { 431 if (profilerPluginData.getStatus() != 0) { 432 return process; 433 } 434 ByteString data = profilerPluginData.getData(); 435 ProcessPluginResult.ProcessData.Builder builder = ProcessPluginResult.ProcessData.newBuilder(); 436 ProcessPluginResult.ProcessData processData = null; 437 try { 438 processData = builder.mergeFrom(data).build(); 439 } catch (InvalidProtocolBufferException exception) { 440 if (ProfilerLogManager.isErrorEnabled()) { 441 LOGGER.info("mergeFrom failed {}", exception.getMessage()); 442 } 443 } 444 List<ProcessPluginResult.ProcessInfo> processesinfoList = processData.getProcessesinfoList(); 445 for (ProcessPluginResult.ProcessInfo processInfoRes : processesinfoList) { 446 ProcessInfo processInfo = new ProcessInfo(); 447 processInfo.setProcessId(processInfoRes.getPid()); 448 processInfo.setProcessName(processInfoRes.getName()); 449 process.add(processInfo); 450 } 451 } 452 return process; 453 } 454 455 /** 456 * Get capabilities 457 * 458 * @param deviceIp deviceIp 459 * @param port port number 460 * @return ProfilerServiceTypes.GetCapabilitiesResponse 461 */ getCapabilities(String deviceIp, int port)462 public ProfilerServiceTypes.GetCapabilitiesResponse getCapabilities(String deviceIp, int port) { 463 if (ProfilerLogManager.isInfoEnabled()) { 464 LOGGER.info("getCapabilities"); 465 } 466 if (port <= 0 || port > LayoutConstants.PORT) { 467 return null; 468 } 469 return getCapabilities(deviceIp, port, 0); 470 } 471 472 /** 473 * Get capabilities 474 * 475 * @param deviceIp deviceIp 476 * @param port port number 477 * @param retryCount retry Count 478 * @return ProfilerServiceTypes.GetCapabilitiesResponse 479 */ getCapabilities(String deviceIp, int port, int retryCount)480 private ProfilerServiceTypes.GetCapabilitiesResponse getCapabilities(String deviceIp, int port, int retryCount) { 481 if (ProfilerLogManager.isInfoEnabled()) { 482 LOGGER.info("getCapabilities"); 483 } 484 int counts = retryCount + 1; 485 ProfilerServiceTypes.GetCapabilitiesResponse response; 486 ProfilerClient client = getProfilerClient(deviceIp, port); 487 try { 488 response = client.getCapabilities( 489 ProfilerServiceTypes.GetCapabilitiesRequest.newBuilder().setRequestId(CommonUtil.getRequestId()) 490 .build()); 491 } catch (StatusRuntimeException exception) { 492 handleGrpcInterface(exception, port, client); 493 if (ProfilerLogManager.isErrorEnabled()) { 494 LOGGER.info("exception Error {}", exception.getMessage()); 495 } 496 if (counts > RETRY_COUNT) { 497 return ProfilerServiceTypes.GetCapabilitiesResponse.newBuilder().build(); 498 } 499 return getCapabilities(deviceIp, port, counts); 500 } 501 return response; 502 } 503 504 /** 505 * keepSession 506 * 507 * @param deviceIp deviceIp 508 * @param port port number 509 * @param sessionId sessionId 510 * @return ProfilerServiceTypes.GetCapabilitiesResponse 511 * @throws StatusRuntimeException StatusRuntimeException 512 */ keepSession(String deviceIp, int port, int sessionId)513 public ProfilerServiceTypes.KeepSessionResponse keepSession(String deviceIp, int port, int sessionId) 514 throws StatusRuntimeException { 515 if (ProfilerLogManager.isInfoEnabled()) { 516 LOGGER.info("keepSession"); 517 } 518 if (port <= 0 || port > LayoutConstants.PORT) { 519 return null; 520 } 521 return keepSession(deviceIp, port, sessionId, 0); 522 } 523 keepSession(String deviceIp, int port, int sessionId, int retryCount)524 private ProfilerServiceTypes.KeepSessionResponse keepSession(String deviceIp, int port, int sessionId, 525 int retryCount) throws StatusRuntimeException { 526 if (ProfilerLogManager.isInfoEnabled()) { 527 LOGGER.info("keepSession"); 528 } 529 int counts = retryCount + 1; 530 ProfilerClient client = getProfilerClient(deviceIp, port); 531 ProfilerServiceTypes.KeepSessionResponse response; 532 try { 533 response = Objects.requireNonNull(client).keepSession( 534 ProfilerServiceTypes.KeepSessionRequest.newBuilder().setRequestId(CommonUtil.getRequestId()) 535 .setSessionId(sessionId).build()); 536 } catch (StatusRuntimeException exception) { 537 handleGrpcInterface(exception, port, client); 538 if (counts > 2 || (exception.getStatus() == INTERNAL && exception.getMessage() 539 .contains("session_id invalid"))) { 540 if (ProfilerLogManager.isErrorEnabled()) { 541 LOGGER.error("exception Error ", exception); 542 } 543 throw exception; 544 } 545 return keepSession(deviceIp, port, sessionId, counts); 546 } 547 return response; 548 } 549 handleGrpcInterface(StatusRuntimeException statusRuntimeException, int port, ProfilerClient client)550 private void handleGrpcInterface(StatusRuntimeException statusRuntimeException, int port, ProfilerClient client) { 551 if (statusRuntimeException.getStatus() != Status.OK && statusRuntimeException.getStatus() != INTERNAL 552 && statusRuntimeException.getStatus() != DEADLINE_EXCEEDED) { 553 if (ProfilerLogManager.isErrorEnabled()) { 554 LOGGER.error("statusRuntimeException", statusRuntimeException); 555 } 556 ManagedChannel channel = client.getChannel(); 557 if (channel.isShutdown() || channel.isTerminated() || statusRuntimeException.getStatus() == UNAVAILABLE) { 558 destroyProfiler("", port); 559 } 560 } 561 } 562 } 563