Skip to content

Commit 173279a

Browse files
Performance Analysis (#555)
* Performance Analysis * u * u * u * u * u * u
1 parent 354b60a commit 173279a

File tree

16 files changed

+346
-11
lines changed

16 files changed

+346
-11
lines changed

center/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ dependencies {
5151
// compile group: 'org.postgresql', name: 'postgresql', version: '42.2.14'
5252

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

5556
compile('org.springdoc:springdoc-openapi-core:1.1.49')
5657
compile('org.springdoc:springdoc-openapi-ui:1.4.1')

center/src/main/java/com/microsoft/hydralab/center/openai/AzureOpenAIServiceClient.java

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
import com.alibaba.fastjson.JSON;
44
import com.alibaba.fastjson.JSONObject;
5+
import com.azure.ai.openai.models.ChatChoice;
6+
import com.azure.ai.openai.models.ChatCompletions;
7+
import com.azure.ai.openai.models.ChatCompletionsOptions;
8+
import com.azure.ai.openai.models.ChatMessage;
9+
import com.azure.ai.openai.models.ChatRole;
10+
import com.microsoft.hydralab.center.openai.data.ChatRequest;
511
import okhttp3.MediaType;
612
import okhttp3.OkHttpClient;
713
import okhttp3.Request;
@@ -10,23 +16,55 @@
1016
import org.slf4j.Logger;
1117
import org.slf4j.LoggerFactory;
1218

19+
import com.azure.ai.openai.OpenAIClient;
20+
import com.azure.ai.openai.OpenAIClientBuilder;
21+
import com.azure.core.credential.AzureKeyCredential;
22+
23+
import java.util.ArrayList;
24+
import java.util.List;
1325
import java.util.Objects;
1426

15-
// Copyright (c) Microsoft Corporation.
27+
// Copyright (c) Microsoft Corporation.
1628
// Licensed under the MIT License.
17-
public class AzureOpenAIServiceClient {
29+
public class AzureOpenAIServiceClient {
1830
public static final String API_VERSION_CHAT = "2023-03-15-preview";
1931
public static final String API_VERSION_IMAGE = "2023-06-01-preview";
2032
private final Logger logger = LoggerFactory.getLogger(AzureOpenAIServiceClient.class);
2133
private final String apiKey;
2234
private final String endpoint;
2335
private final String deployment;
2436
OkHttpClient client = new OkHttpClient();
37+
private OpenAIClient azureClient = null;
2538

2639
public AzureOpenAIServiceClient(String apiKey, String deployment, String endpoint) {
27-
this.apiKey = apiKey;
28-
this.endpoint = endpoint.endsWith("/") ? endpoint.substring(0, endpoint.length() - 1) : endpoint;
29-
this.deployment = deployment;
40+
this.apiKey = apiKey == null ? "" : apiKey;
41+
this.endpoint = endpoint == null ? "" : endpoint.endsWith("/") ? endpoint.substring(0, endpoint.length() - 1) : endpoint;
42+
this.deployment = deployment == null ? "" : deployment;
43+
if (!apiKey.isEmpty()) {
44+
this.azureClient = new OpenAIClientBuilder()
45+
.endpoint(endpoint)
46+
.credential(new AzureKeyCredential(apiKey))
47+
.buildClient();
48+
}
49+
}
50+
51+
public String completion(String question) {
52+
if (azureClient == null) {
53+
return "";
54+
}
55+
List<ChatMessage> chatMessages = new ArrayList<>();
56+
chatMessages.add(new ChatMessage(ChatRole.SYSTEM, "You are a helpful assistant."));
57+
chatMessages.add(new ChatMessage(ChatRole.USER, question));
58+
59+
ChatCompletionsOptions options = new ChatCompletionsOptions(chatMessages);
60+
options.setN(1);
61+
62+
ChatCompletions chatCompletions = azureClient.getChatCompletions(deployment, options);
63+
64+
for (ChatChoice choice : chatCompletions.getChoices()) {
65+
return choice.getMessage().getContent();
66+
}
67+
return "";
3068
}
3169

3270
public String chatCompletion(ChatRequest request) {
@@ -36,13 +74,10 @@ public String chatCompletion(ChatRequest request) {
3674
private String callAzureOpenAIAPI(String operation, String requestBodyString, String apiVersion) {
3775
MediaType mediaType = MediaType.parse("application/json");
3876
String url = String.format("%s/openai/deployments/%s/%s?api-version=%s", endpoint, deployment, operation, apiVersion);
39-
4077
logger.info("Request body: {}", requestBodyString);
41-
4278
RequestBody body = RequestBody.create(requestBodyString, mediaType);
4379
Request httpRequest = new Request.Builder().url(url).post(body)
4480
.addHeader("api-key", apiKey).build();
45-
4681
try (Response response = client.newCall(httpRequest).execute()) {
4782
if (!response.isSuccessful()) {
4883
throw new RuntimeException("Unexpected response code: " + response);
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
package com.microsoft.hydralab.center.openai;
2+
3+
import com.alibaba.fastjson.JSON;
4+
import com.alibaba.fastjson.JSONArray;
5+
import com.alibaba.fastjson.JSONObject;
6+
import com.microsoft.hydralab.center.openai.data.ExceptionSuggestion;
7+
import com.microsoft.hydralab.center.openai.data.SimplifiedPerformanceDataSet;
8+
import com.microsoft.hydralab.center.openai.data.SimplifiedPerformanceResult;
9+
import com.microsoft.hydralab.common.entity.common.StorageFileInfo;
10+
import com.microsoft.hydralab.common.entity.common.TestRun;
11+
import com.microsoft.hydralab.common.file.StorageServiceClientProxy;
12+
import com.microsoft.hydralab.common.file.impl.AzureOpenaiConfig;
13+
import com.microsoft.hydralab.common.repository.StorageFileInfoRepository;
14+
import com.microsoft.hydralab.common.util.Const;
15+
import com.microsoft.hydralab.common.util.HydraLabRuntimeException;
16+
import com.microsoft.hydralab.performance.PerformanceInspectionResult;
17+
import com.microsoft.hydralab.performance.PerformanceResultParser;
18+
import com.microsoft.hydralab.performance.PerformanceTestResult;
19+
import com.microsoft.hydralab.performance.entity.AndroidBatteryInfo;
20+
import com.microsoft.hydralab.performance.entity.AndroidHprofMemoryInfo;
21+
import com.microsoft.hydralab.performance.entity.AndroidMemoryInfo;
22+
import com.microsoft.hydralab.performance.entity.IOSEnergyGaugeInfo;
23+
import com.microsoft.hydralab.performance.entity.IOSMemoryPerfInfo;
24+
import com.microsoft.hydralab.performance.entity.WindowsBatteryParsedData;
25+
import com.microsoft.hydralab.performance.entity.WindowsMemoryParsedData;
26+
import org.springframework.stereotype.Service;
27+
28+
import javax.annotation.Resource;
29+
import java.io.BufferedReader;
30+
import java.io.File;
31+
import java.io.FileNotFoundException;
32+
import java.io.FileReader;
33+
import java.io.IOException;
34+
import java.util.ArrayList;
35+
import java.util.HashMap;
36+
import java.util.List;
37+
import java.util.Map;
38+
39+
import org.springframework.context.ApplicationContext;
40+
41+
import static com.microsoft.hydralab.center.util.CenterConstant.CENTER_TEMP_FILE_DIR;
42+
43+
@Service
44+
public class SuggestionService {
45+
@Resource
46+
StorageFileInfoRepository storageFileInfoRepository;
47+
@Resource
48+
StorageServiceClientProxy storageServiceClientProxy;
49+
private final AzureOpenaiConfig openaiConfig;
50+
private AzureOpenAIServiceClient oaiClient = null;
51+
private final Map<PerformanceResultParser.PerformanceResultParserType, Class> performanceTypeMap = Map.ofEntries(
52+
Map.entry(PerformanceResultParser.PerformanceResultParserType.PARSER_ANDROID_BATTERY_INFO, AndroidBatteryInfo.class),
53+
Map.entry(PerformanceResultParser.PerformanceResultParserType.PARSER_WIN_MEMORY, WindowsMemoryParsedData.class),
54+
Map.entry(PerformanceResultParser.PerformanceResultParserType.PARSER_WIN_BATTERY, WindowsBatteryParsedData.class),
55+
Map.entry(PerformanceResultParser.PerformanceResultParserType.PARSER_ANDROID_MEMORY_INFO, AndroidMemoryInfo.class),
56+
Map.entry(PerformanceResultParser.PerformanceResultParserType.PARSER_ANDROID_MEMORY_DUMP, AndroidHprofMemoryInfo.class),
57+
Map.entry(PerformanceResultParser.PerformanceResultParserType.PARSER_IOS_ENERGY, IOSEnergyGaugeInfo.class),
58+
Map.entry(PerformanceResultParser.PerformanceResultParserType.PARSER_IOS_MEMORY, IOSMemoryPerfInfo.class)
59+
);
60+
61+
public SuggestionService(ApplicationContext applicationContext) {
62+
this.openaiConfig = applicationContext.getBean(Const.AzureOpenaiConfig.AZURE_OPENAI_CONFIG, AzureOpenaiConfig.class);
63+
if (openaiConfig.getApiKey() != null && openaiConfig.getDeployment() != null && openaiConfig.getEndpoint() != null) {
64+
this.oaiClient = new AzureOpenAIServiceClient(
65+
openaiConfig.getApiKey(),
66+
openaiConfig.getDeployment(),
67+
openaiConfig.getEndpoint());
68+
}
69+
}
70+
71+
public void performanceAnalyze(TestRun testRun) {
72+
List<PerformanceTestResult> performanceResults = getPerformanceResult(testRun);
73+
if (performanceResults == null) {
74+
return;
75+
}
76+
String perfsString = convertPerformanceTestToJsonString(performanceResults);
77+
String perfSuggestion = getOpenaiPerformanceSuggestion(perfsString);
78+
if (perfSuggestion != null) {
79+
testRun.setSuggestion(perfSuggestion);
80+
}
81+
}
82+
83+
public ExceptionSuggestion exceptionAnalyze(TestRun testRun) {
84+
ExceptionSuggestion suggestion = new ExceptionSuggestion();
85+
return suggestion;
86+
}
87+
88+
@SuppressWarnings("unchecked")
89+
private List<PerformanceTestResult> getPerformanceResult(TestRun testRun) {
90+
List<PerformanceTestResult> performanceTestResult = null;
91+
92+
String fileId = "";
93+
for (StorageFileInfo f : testRun.getAttachments()) {
94+
if (f.getFileName().contains(Const.PerformanceConfig.DEFAULT_FILE_NAME)) {
95+
fileId = f.getFileId();
96+
break;
97+
}
98+
}
99+
if (fileId.isEmpty()) {
100+
return null;
101+
}
102+
103+
StorageFileInfo perfBlobFile = storageFileInfoRepository.findById(fileId).orElse(null);
104+
if (perfBlobFile == null) {
105+
throw new HydraLabRuntimeException("Graph zip file not exist!");
106+
}
107+
File perfFile = new File(CENTER_TEMP_FILE_DIR, perfBlobFile.getBlobPath());
108+
if (!perfFile.exists()) {
109+
storageServiceClientProxy.download(perfFile, perfBlobFile);
110+
}
111+
if (!perfFile.exists()) {
112+
return null;
113+
}
114+
115+
try (FileReader reader = new FileReader(perfFile)) {
116+
BufferedReader br = new BufferedReader(reader);
117+
String line;
118+
StringBuilder sb = new StringBuilder();
119+
while ((line = br.readLine()) != null) {
120+
sb.append(line);
121+
}
122+
br.close();
123+
String jsonString = sb.toString();
124+
125+
List<PerformanceTestResult> results = JSON.parseArray(jsonString, PerformanceTestResult.class);
126+
JSONArray ja = JSON.parseArray(jsonString);
127+
for (int i = 0; i < ja.size(); i++) {
128+
PerformanceTestResult result = results.get(i);
129+
Class classType = this.performanceTypeMap.get(result.parserType);
130+
JSONObject jo = (JSONObject)ja.get(i);
131+
List<Object> inspects = new ArrayList<Object>();
132+
Object performanceInspectionResults = jo.get("performanceInspectionResults");
133+
if (performanceInspectionResults instanceof List<?>) {
134+
inspects = (List<Object>)performanceInspectionResults;
135+
}
136+
for (int j = 0; j < inspects.size(); j++) {
137+
JSONObject inspect = (JSONObject)inspects.get(j);
138+
if (inspect == null) {
139+
continue;
140+
}
141+
JSONObject parsedData = (JSONObject)inspect.get("parsedData");
142+
if (parsedData == null) {
143+
continue;
144+
}
145+
result.performanceInspectionResults.get(j).parsedData = parsedData.toJavaObject(classType);
146+
}
147+
}
148+
performanceTestResult = results;
149+
} catch (FileNotFoundException e) {
150+
e.printStackTrace();
151+
} catch (IOException e) {
152+
e.printStackTrace();
153+
} catch (Exception e) {
154+
e.printStackTrace();
155+
}
156+
return performanceTestResult;
157+
}
158+
159+
private String convertPerformanceTestToJsonString(List<PerformanceTestResult> results) {
160+
List<SimplifiedPerformanceResult> sResults = new ArrayList<>();
161+
for (PerformanceTestResult result : results) {
162+
SimplifiedPerformanceResult sResult = new SimplifiedPerformanceResult();
163+
List<SimplifiedPerformanceDataSet> dataSetArr = new ArrayList<>();
164+
for (PerformanceInspectionResult ins : result.performanceInspectionResults) {
165+
SimplifiedPerformanceDataSet ds = new SimplifiedPerformanceDataSet();
166+
if (ins.parsedData instanceof AndroidMemoryInfo) {
167+
AndroidMemoryInfo info = (AndroidMemoryInfo)ins.parsedData;
168+
insertPerformanceData(ds, "CodePss", info.getCodePss());
169+
insertPerformanceData(ds, "CodeRss", info.getCodeRss());
170+
insertPerformanceData(ds, "GraphicsPss", info.getGraphicsPss());
171+
insertPerformanceData(ds, "GraphicsRss", info.getGraphicsRss());
172+
insertPerformanceData(ds, "StackPss", info.getStackPss());
173+
insertPerformanceData(ds, "StackRss", info.getStackRss());
174+
insertPerformanceData(ds, "HeapPss", info.getJavaHeapPss());
175+
insertPerformanceData(ds, "HeapRss", info.getJavaHeapRss());
176+
insertPerformanceData(ds, "SystemPss", info.getSystemPss());
177+
insertPerformanceData(ds, "SystemRss", info.getSystemRss());
178+
} else if (ins.parsedData instanceof AndroidBatteryInfo) {
179+
AndroidBatteryInfo info = (AndroidBatteryInfo)ins.parsedData;
180+
insertPerformanceData(ds, "Cpu", info.getCpu());
181+
insertPerformanceData(ds, "Ratio", info.getRatio());
182+
insertPerformanceData(ds, "AppUsage", info.getAppUsage());
183+
insertPerformanceData(ds, "WakeLock", info.getWakeLock());
184+
} else {
185+
continue;
186+
}
187+
ds.setTimestamp(ins.timestamp);
188+
dataSetArr.add(ds);
189+
}
190+
191+
sResult.setType(result.inspectorType.toString());
192+
sResult.setDataset(dataSetArr);
193+
sResults.add(sResult);
194+
}
195+
return JSON.toJSONString(sResults);
196+
}
197+
198+
private void insertPerformanceData(SimplifiedPerformanceDataSet ds, String name, Object data) {
199+
if (ds.getInspect() == null) {
200+
ds.setInspect(new HashMap<String, Object>());
201+
}
202+
ds.getInspect().put(name, data);
203+
}
204+
205+
private String getOpenaiPerformanceSuggestion(String perfSuggestion) {
206+
if (this.oaiClient != null) {
207+
return oaiClient.completion(perfSuggestion);
208+
}
209+
return null;
210+
}
211+
}

center/src/main/java/com/microsoft/hydralab/center/openai/ChatMessage.java renamed to center/src/main/java/com/microsoft/hydralab/center/openai/data/ChatMessage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.microsoft.hydralab.center.openai;
1+
package com.microsoft.hydralab.center.openai.data;
22

33
import lombok.Data;
44

center/src/main/java/com/microsoft/hydralab/center/openai/ChatRequest.java renamed to center/src/main/java/com/microsoft/hydralab/center/openai/data/ChatRequest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.microsoft.hydralab.center.openai;
1+
package com.microsoft.hydralab.center.openai.data;
22

33
import com.alibaba.fastjson.annotation.JSONField;
44
import lombok.Data;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.microsoft.hydralab.center.openai.data;
2+
3+
import com.alibaba.fastjson.annotation.JSONField;
4+
import lombok.Data;
5+
6+
@Data
7+
public class ExceptionSuggestion {
8+
@JSONField(name = "content")
9+
private String content = "";
10+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.microsoft.hydralab.center.openai.data;
2+
3+
import com.alibaba.fastjson.annotation.JSONField;
4+
import lombok.Data;
5+
6+
import java.util.Map;
7+
8+
@Data
9+
public class SimplifiedPerformanceDataSet {
10+
@JSONField(name = "timestamp")
11+
private long timestamp;
12+
@JSONField(name = "inspect")
13+
private Map<String, Object> inspect;
14+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.microsoft.hydralab.center.openai.data;
2+
3+
import com.alibaba.fastjson.annotation.JSONField;
4+
import lombok.Data;
5+
6+
import java.util.List;
7+
8+
@Data
9+
public class SimplifiedPerformanceResult {
10+
@JSONField(name = "type")
11+
private String type = "";
12+
13+
@JSONField(name = "dataset")
14+
private List<SimplifiedPerformanceDataSet> dataset;
15+
}

center/src/main/java/com/microsoft/hydralab/center/service/DeviceAgentManagementService.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.alibaba.fastjson.JSONArray;
88
import com.alibaba.fastjson.JSONObject;
99
import com.android.ddmlib.IDevice;
10+
import com.microsoft.hydralab.center.openai.SuggestionService;
1011
import com.microsoft.hydralab.center.repository.AgentUserRepository;
1112
import com.microsoft.hydralab.center.util.MetricUtil;
1213
import com.microsoft.hydralab.common.entity.agent.MobileDevice;
@@ -110,6 +111,9 @@ public class DeviceAgentManagementService {
110111
StorageServiceClientProxy storageServiceClientProxy;
111112
@Resource
112113
StorageTokenManageService storageTokenManageService;
114+
@Resource
115+
SuggestionService suggestionService;
116+
113117
@Value("${app.storage.type}")
114118
private String storageType;
115119

@@ -283,11 +287,13 @@ private void handleQualifiedAgentMessage(Message message, AgentSessionInfo saved
283287
TestTask testTask = (TestTask) message.getBody();
284288
boolean isFinished = testTask.getStatus().equals(TestTask.TestStatus.FINISHED);
285289
testDataService.saveTestTaskDataFromAgent(testTask, isFinished, savedSession.agentUser.getId());
286-
287290
//after the task finishing, update the status of device used
288291
if (isFinished) {
289292
List<TestRun> deviceTestResults = testTask.getDeviceTestResults();
290293
for (TestRun deviceTestResult : deviceTestResults) {
294+
if (testTask.isEnablePerformanceSuggestion()) {
295+
suggestionService.performanceAnalyze(deviceTestResult);
296+
}
291297
String[] identifiers = deviceTestResult.getDeviceSerialNumber().split(",");
292298
for (String identifier : identifiers) {
293299
updateDeviceStatus(identifier, DeviceInfo.ONLINE, null);

0 commit comments

Comments
 (0)