Skip to content

Commit

Permalink
Performance Analysis (#555)
Browse files Browse the repository at this point in the history
* Performance Analysis

* u

* u

* u

* u

* u

* u
  • Loading branch information
DexterDreeeam authored Sep 12, 2023
1 parent 354b60a commit 173279a
Show file tree
Hide file tree
Showing 16 changed files with 346 additions and 11 deletions.
1 change: 1 addition & 0 deletions center/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ dependencies {
// compile group: 'org.postgresql', name: 'postgresql', version: '42.2.14'

compile group: 'org.springframework.security', name: 'spring-security-oauth2-client', version: '5.2.2.RELEASE'
compile group: 'com.azure', name: 'azure-ai-openai', version: '1.0.0-beta.3'

compile('org.springdoc:springdoc-openapi-core:1.1.49')
compile('org.springdoc:springdoc-openapi-ui:1.4.1')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.azure.ai.openai.models.ChatChoice;
import com.azure.ai.openai.models.ChatCompletions;
import com.azure.ai.openai.models.ChatCompletionsOptions;
import com.azure.ai.openai.models.ChatMessage;
import com.azure.ai.openai.models.ChatRole;
import com.microsoft.hydralab.center.openai.data.ChatRequest;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
Expand All @@ -10,23 +16,55 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.azure.ai.openai.OpenAIClient;
import com.azure.ai.openai.OpenAIClientBuilder;
import com.azure.core.credential.AzureKeyCredential;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

// Copyright (c) Microsoft Corporation.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
public class AzureOpenAIServiceClient {
public class AzureOpenAIServiceClient {
public static final String API_VERSION_CHAT = "2023-03-15-preview";
public static final String API_VERSION_IMAGE = "2023-06-01-preview";
private final Logger logger = LoggerFactory.getLogger(AzureOpenAIServiceClient.class);
private final String apiKey;
private final String endpoint;
private final String deployment;
OkHttpClient client = new OkHttpClient();
private OpenAIClient azureClient = null;

public AzureOpenAIServiceClient(String apiKey, String deployment, String endpoint) {
this.apiKey = apiKey;
this.endpoint = endpoint.endsWith("/") ? endpoint.substring(0, endpoint.length() - 1) : endpoint;
this.deployment = deployment;
this.apiKey = apiKey == null ? "" : apiKey;
this.endpoint = endpoint == null ? "" : endpoint.endsWith("/") ? endpoint.substring(0, endpoint.length() - 1) : endpoint;
this.deployment = deployment == null ? "" : deployment;
if (!apiKey.isEmpty()) {
this.azureClient = new OpenAIClientBuilder()
.endpoint(endpoint)
.credential(new AzureKeyCredential(apiKey))
.buildClient();
}
}

public String completion(String question) {
if (azureClient == null) {
return "";
}
List<ChatMessage> chatMessages = new ArrayList<>();
chatMessages.add(new ChatMessage(ChatRole.SYSTEM, "You are a helpful assistant."));
chatMessages.add(new ChatMessage(ChatRole.USER, question));

ChatCompletionsOptions options = new ChatCompletionsOptions(chatMessages);
options.setN(1);

ChatCompletions chatCompletions = azureClient.getChatCompletions(deployment, options);

for (ChatChoice choice : chatCompletions.getChoices()) {
return choice.getMessage().getContent();
}
return "";
}

public String chatCompletion(ChatRequest request) {
Expand All @@ -36,13 +74,10 @@ public String chatCompletion(ChatRequest request) {
private String callAzureOpenAIAPI(String operation, String requestBodyString, String apiVersion) {
MediaType mediaType = MediaType.parse("application/json");
String url = String.format("%s/openai/deployments/%s/%s?api-version=%s", endpoint, deployment, operation, apiVersion);

logger.info("Request body: {}", requestBodyString);

RequestBody body = RequestBody.create(requestBodyString, mediaType);
Request httpRequest = new Request.Builder().url(url).post(body)
.addHeader("api-key", apiKey).build();

try (Response response = client.newCall(httpRequest).execute()) {
if (!response.isSuccessful()) {
throw new RuntimeException("Unexpected response code: " + response);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package com.microsoft.hydralab.center.openai;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.microsoft.hydralab.center.openai.data.ExceptionSuggestion;
import com.microsoft.hydralab.center.openai.data.SimplifiedPerformanceDataSet;
import com.microsoft.hydralab.center.openai.data.SimplifiedPerformanceResult;
import com.microsoft.hydralab.common.entity.common.StorageFileInfo;
import com.microsoft.hydralab.common.entity.common.TestRun;
import com.microsoft.hydralab.common.file.StorageServiceClientProxy;
import com.microsoft.hydralab.common.file.impl.AzureOpenaiConfig;
import com.microsoft.hydralab.common.repository.StorageFileInfoRepository;
import com.microsoft.hydralab.common.util.Const;
import com.microsoft.hydralab.common.util.HydraLabRuntimeException;
import com.microsoft.hydralab.performance.PerformanceInspectionResult;
import com.microsoft.hydralab.performance.PerformanceResultParser;
import com.microsoft.hydralab.performance.PerformanceTestResult;
import com.microsoft.hydralab.performance.entity.AndroidBatteryInfo;
import com.microsoft.hydralab.performance.entity.AndroidHprofMemoryInfo;
import com.microsoft.hydralab.performance.entity.AndroidMemoryInfo;
import com.microsoft.hydralab.performance.entity.IOSEnergyGaugeInfo;
import com.microsoft.hydralab.performance.entity.IOSMemoryPerfInfo;
import com.microsoft.hydralab.performance.entity.WindowsBatteryParsedData;
import com.microsoft.hydralab.performance.entity.WindowsMemoryParsedData;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.context.ApplicationContext;

import static com.microsoft.hydralab.center.util.CenterConstant.CENTER_TEMP_FILE_DIR;

@Service
public class SuggestionService {
@Resource
StorageFileInfoRepository storageFileInfoRepository;
@Resource
StorageServiceClientProxy storageServiceClientProxy;
private final AzureOpenaiConfig openaiConfig;
private AzureOpenAIServiceClient oaiClient = null;
private final Map<PerformanceResultParser.PerformanceResultParserType, Class> performanceTypeMap = Map.ofEntries(
Map.entry(PerformanceResultParser.PerformanceResultParserType.PARSER_ANDROID_BATTERY_INFO, AndroidBatteryInfo.class),
Map.entry(PerformanceResultParser.PerformanceResultParserType.PARSER_WIN_MEMORY, WindowsMemoryParsedData.class),
Map.entry(PerformanceResultParser.PerformanceResultParserType.PARSER_WIN_BATTERY, WindowsBatteryParsedData.class),
Map.entry(PerformanceResultParser.PerformanceResultParserType.PARSER_ANDROID_MEMORY_INFO, AndroidMemoryInfo.class),
Map.entry(PerformanceResultParser.PerformanceResultParserType.PARSER_ANDROID_MEMORY_DUMP, AndroidHprofMemoryInfo.class),
Map.entry(PerformanceResultParser.PerformanceResultParserType.PARSER_IOS_ENERGY, IOSEnergyGaugeInfo.class),
Map.entry(PerformanceResultParser.PerformanceResultParserType.PARSER_IOS_MEMORY, IOSMemoryPerfInfo.class)
);

public SuggestionService(ApplicationContext applicationContext) {
this.openaiConfig = applicationContext.getBean(Const.AzureOpenaiConfig.AZURE_OPENAI_CONFIG, AzureOpenaiConfig.class);
if (openaiConfig.getApiKey() != null && openaiConfig.getDeployment() != null && openaiConfig.getEndpoint() != null) {
this.oaiClient = new AzureOpenAIServiceClient(
openaiConfig.getApiKey(),
openaiConfig.getDeployment(),
openaiConfig.getEndpoint());
}
}

public void performanceAnalyze(TestRun testRun) {
List<PerformanceTestResult> performanceResults = getPerformanceResult(testRun);
if (performanceResults == null) {
return;
}
String perfsString = convertPerformanceTestToJsonString(performanceResults);
String perfSuggestion = getOpenaiPerformanceSuggestion(perfsString);
if (perfSuggestion != null) {
testRun.setSuggestion(perfSuggestion);
}
}

public ExceptionSuggestion exceptionAnalyze(TestRun testRun) {
ExceptionSuggestion suggestion = new ExceptionSuggestion();
return suggestion;
}

@SuppressWarnings("unchecked")
private List<PerformanceTestResult> getPerformanceResult(TestRun testRun) {
List<PerformanceTestResult> performanceTestResult = null;

String fileId = "";
for (StorageFileInfo f : testRun.getAttachments()) {
if (f.getFileName().contains(Const.PerformanceConfig.DEFAULT_FILE_NAME)) {
fileId = f.getFileId();
break;
}
}
if (fileId.isEmpty()) {
return null;
}

StorageFileInfo perfBlobFile = storageFileInfoRepository.findById(fileId).orElse(null);
if (perfBlobFile == null) {
throw new HydraLabRuntimeException("Graph zip file not exist!");
}
File perfFile = new File(CENTER_TEMP_FILE_DIR, perfBlobFile.getBlobPath());
if (!perfFile.exists()) {
storageServiceClientProxy.download(perfFile, perfBlobFile);
}
if (!perfFile.exists()) {
return null;
}

try (FileReader reader = new FileReader(perfFile)) {
BufferedReader br = new BufferedReader(reader);
String line;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
br.close();
String jsonString = sb.toString();

List<PerformanceTestResult> results = JSON.parseArray(jsonString, PerformanceTestResult.class);
JSONArray ja = JSON.parseArray(jsonString);
for (int i = 0; i < ja.size(); i++) {
PerformanceTestResult result = results.get(i);
Class classType = this.performanceTypeMap.get(result.parserType);
JSONObject jo = (JSONObject)ja.get(i);
List<Object> inspects = new ArrayList<Object>();
Object performanceInspectionResults = jo.get("performanceInspectionResults");
if (performanceInspectionResults instanceof List<?>) {
inspects = (List<Object>)performanceInspectionResults;
}
for (int j = 0; j < inspects.size(); j++) {
JSONObject inspect = (JSONObject)inspects.get(j);
if (inspect == null) {
continue;
}
JSONObject parsedData = (JSONObject)inspect.get("parsedData");
if (parsedData == null) {
continue;
}
result.performanceInspectionResults.get(j).parsedData = parsedData.toJavaObject(classType);
}
}
performanceTestResult = results;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return performanceTestResult;
}

private String convertPerformanceTestToJsonString(List<PerformanceTestResult> results) {
List<SimplifiedPerformanceResult> sResults = new ArrayList<>();
for (PerformanceTestResult result : results) {
SimplifiedPerformanceResult sResult = new SimplifiedPerformanceResult();
List<SimplifiedPerformanceDataSet> dataSetArr = new ArrayList<>();
for (PerformanceInspectionResult ins : result.performanceInspectionResults) {
SimplifiedPerformanceDataSet ds = new SimplifiedPerformanceDataSet();
if (ins.parsedData instanceof AndroidMemoryInfo) {
AndroidMemoryInfo info = (AndroidMemoryInfo)ins.parsedData;
insertPerformanceData(ds, "CodePss", info.getCodePss());
insertPerformanceData(ds, "CodeRss", info.getCodeRss());
insertPerformanceData(ds, "GraphicsPss", info.getGraphicsPss());
insertPerformanceData(ds, "GraphicsRss", info.getGraphicsRss());
insertPerformanceData(ds, "StackPss", info.getStackPss());
insertPerformanceData(ds, "StackRss", info.getStackRss());
insertPerformanceData(ds, "HeapPss", info.getJavaHeapPss());
insertPerformanceData(ds, "HeapRss", info.getJavaHeapRss());
insertPerformanceData(ds, "SystemPss", info.getSystemPss());
insertPerformanceData(ds, "SystemRss", info.getSystemRss());
} else if (ins.parsedData instanceof AndroidBatteryInfo) {
AndroidBatteryInfo info = (AndroidBatteryInfo)ins.parsedData;
insertPerformanceData(ds, "Cpu", info.getCpu());
insertPerformanceData(ds, "Ratio", info.getRatio());
insertPerformanceData(ds, "AppUsage", info.getAppUsage());
insertPerformanceData(ds, "WakeLock", info.getWakeLock());
} else {
continue;
}
ds.setTimestamp(ins.timestamp);
dataSetArr.add(ds);
}

sResult.setType(result.inspectorType.toString());
sResult.setDataset(dataSetArr);
sResults.add(sResult);
}
return JSON.toJSONString(sResults);
}

private void insertPerformanceData(SimplifiedPerformanceDataSet ds, String name, Object data) {
if (ds.getInspect() == null) {
ds.setInspect(new HashMap<String, Object>());
}
ds.getInspect().put(name, data);
}

private String getOpenaiPerformanceSuggestion(String perfSuggestion) {
if (this.oaiClient != null) {
return oaiClient.completion(perfSuggestion);
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.microsoft.hydralab.center.openai;
package com.microsoft.hydralab.center.openai.data;

import lombok.Data;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.microsoft.hydralab.center.openai;
package com.microsoft.hydralab.center.openai.data;

import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.microsoft.hydralab.center.openai.data;

import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;

@Data
public class ExceptionSuggestion {
@JSONField(name = "content")
private String content = "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.microsoft.hydralab.center.openai.data;

import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;

import java.util.Map;

@Data
public class SimplifiedPerformanceDataSet {
@JSONField(name = "timestamp")
private long timestamp;
@JSONField(name = "inspect")
private Map<String, Object> inspect;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.microsoft.hydralab.center.openai.data;

import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;

import java.util.List;

@Data
public class SimplifiedPerformanceResult {
@JSONField(name = "type")
private String type = "";

@JSONField(name = "dataset")
private List<SimplifiedPerformanceDataSet> dataset;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.android.ddmlib.IDevice;
import com.microsoft.hydralab.center.openai.SuggestionService;
import com.microsoft.hydralab.center.repository.AgentUserRepository;
import com.microsoft.hydralab.center.util.MetricUtil;
import com.microsoft.hydralab.common.entity.agent.MobileDevice;
Expand Down Expand Up @@ -110,6 +111,9 @@ public class DeviceAgentManagementService {
StorageServiceClientProxy storageServiceClientProxy;
@Resource
StorageTokenManageService storageTokenManageService;
@Resource
SuggestionService suggestionService;

@Value("${app.storage.type}")
private String storageType;

Expand Down Expand Up @@ -283,11 +287,13 @@ private void handleQualifiedAgentMessage(Message message, AgentSessionInfo saved
TestTask testTask = (TestTask) message.getBody();
boolean isFinished = testTask.getStatus().equals(TestTask.TestStatus.FINISHED);
testDataService.saveTestTaskDataFromAgent(testTask, isFinished, savedSession.agentUser.getId());

//after the task finishing, update the status of device used
if (isFinished) {
List<TestRun> deviceTestResults = testTask.getDeviceTestResults();
for (TestRun deviceTestResult : deviceTestResults) {
if (testTask.isEnablePerformanceSuggestion()) {
suggestionService.performanceAnalyze(deviceTestResult);
}
String[] identifiers = deviceTestResult.getDeviceSerialNumber().split(",");
for (String identifier : identifiers) {
updateDeviceStatus(identifier, DeviceInfo.ONLINE, null);
Expand Down
Loading

0 comments on commit 173279a

Please sign in to comment.