Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add android meminfo inspector and parser #320

Merged
merged 2 commits into from
Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.microsoft.hydralab.performance.Entity;

import lombok.Data;

import java.io.Serializable;

@Data
public class AndroidMemoryInfo implements Serializable {
private String appPackageName;
private long timeStamp;
private String description;
/**
* Index mapping is shown as below:
* <p>
* 0: Java Heap PSS; 1. Java Heap RSS;
* 2. Native Heap PSS; 3. Native Heap RSS;
* 4. Code PSS; 5. Code RSS;
* 6. Stack PSS; 7. Stack RSS;
* 8. Graphics PSS; 9. Graphics RSS;
* 10. Private Other PSS; 11. Private Other RSS;
* 12. System PSS; 13. System RSS;
* 14. Unknown PSS; 15. Unknown RSS;
* 16. TOTAL PSS; 17. TOTAL RSS; 18. TOTAL SWAP PSS;
* <p>
* Since blanks exist in memory summary, some data may not be fetched, default value 0 is then applied.
*/
private long javaHeapPss;
private long javaHeapRss;
private long nativeHeapPss;
private long nativeHeapRss;
private long codePss;
private long codeRss;
private long stackPss;
private long stackRss;
private long graphicsPss;
private long graphicsRss;
private long privateOtherPss;
private long privateOtherRss;
private long systemPss;
private long systemRss;
private long unknownPss;
private long unknownRss;
private long totalPss;
private long totalRss;
private long totalSwapPss;

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
import com.microsoft.hydralab.common.util.FileUtil;
import com.microsoft.hydralab.common.util.ThreadPoolUtil;
import com.microsoft.hydralab.performance.inspectors.AndroidBatteryInfoInspector;
import com.microsoft.hydralab.performance.inspectors.AndroidMemoryInfoInspector;
import com.microsoft.hydralab.performance.inspectors.WindowsBatteryInspector;
import com.microsoft.hydralab.performance.inspectors.WindowsMemoryInspector;
import com.microsoft.hydralab.performance.parsers.AndroidBatteryInfoResultParser;
import com.microsoft.hydralab.performance.parsers.AndroidMemoryInfoResultParser;
import com.microsoft.hydralab.performance.parsers.WindowsBatteryResultParser;
import com.microsoft.hydralab.performance.parsers.WindowsMemoryResultParser;
import org.jetbrains.annotations.NotNull;
Expand All @@ -31,17 +33,20 @@ public class PerformanceTestManagementService implements IPerformanceInspectionS
private static final Map<PerformanceInspector.PerformanceInspectorType, PerformanceResultParser.PerformanceResultParserType> inspectorParserTypeMap = Map.of(
INSPECTOR_ANDROID_BATTERY_INFO, PARSER_ANDROID_BATTERY_INFO,
INSPECTOR_WIN_MEMORY, PARSER_WIN_MEMORY,
INSPECTOR_WIN_BATTERY, PARSER_WIN_BATTERY
INSPECTOR_WIN_BATTERY, PARSER_WIN_BATTERY,
INSPECTOR_ANDROID_MEMORY_INFO, PARSER_ANDROID_MEMORY_INFO
);
private final Map<PerformanceInspector.PerformanceInspectorType, PerformanceInspector> performanceInspectorMap = Map.of(
INSPECTOR_ANDROID_BATTERY_INFO, new AndroidBatteryInfoInspector(),
INSPECTOR_WIN_MEMORY, new WindowsMemoryInspector(),
INSPECTOR_WIN_BATTERY, new WindowsBatteryInspector()
INSPECTOR_WIN_BATTERY, new WindowsBatteryInspector(),
INSPECTOR_ANDROID_MEMORY_INFO, new AndroidMemoryInfoInspector()
);
private final Map<PerformanceResultParser.PerformanceResultParserType, PerformanceResultParser> performanceResultParserMap = Map.of(
PARSER_ANDROID_BATTERY_INFO, new AndroidBatteryInfoResultParser(),
PARSER_WIN_MEMORY, new WindowsMemoryResultParser(),
PARSER_WIN_BATTERY, new WindowsBatteryResultParser()
PARSER_WIN_BATTERY, new WindowsBatteryResultParser(),
PARSER_ANDROID_MEMORY_INFO, new AndroidMemoryInfoResultParser()
);

private final Map<String, List<ScheduledFuture<?>>> inspectPerformanceTimerMap = new ConcurrentHashMap<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.microsoft.hydralab.performance.inspectors;

import com.microsoft.hydralab.common.util.ShellUtils;
import com.microsoft.hydralab.common.util.TimeUtils;
import com.microsoft.hydralab.performance.PerformanceInspection;
import com.microsoft.hydralab.performance.PerformanceInspectionResult;
import com.microsoft.hydralab.performance.PerformanceInspector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import java.io.File;

public class AndroidMemoryInfoInspector implements PerformanceInspector {

private static final String RAW_RESULT_FILE_NAME_FORMAT = "%s_%s_%s_memory.txt";
private final Logger classLogger = LoggerFactory.getLogger(getClass());


@Override
public PerformanceInspectionResult inspect(PerformanceInspection performanceInspection) {

File rawResultFolder = new File(performanceInspection.resultFolder, performanceInspection.appId);
Assert.isTrue(rawResultFolder.exists() || rawResultFolder.mkdir(), "rawResultFolder.mkdirs() failed in" + rawResultFolder.getAbsolutePath());
File rawResultFile = new File(rawResultFolder,
String.format(RAW_RESULT_FILE_NAME_FORMAT, getClass().getSimpleName(), performanceInspection.appId, TimeUtils.getTimestampForFilename()));

ShellUtils.execLocalCommandWithResult(String.format(getMemInfoCommand(),
performanceInspection.deviceIdentifier, performanceInspection.appId, rawResultFile.getAbsolutePath()), classLogger);
return new PerformanceInspectionResult(rawResultFile, performanceInspection);
}

private String getMemInfoCommand() {
String format;
if (ShellUtils.isConnectedToWindowsOS) {
format = "adb -s %s shell dumpsys meminfo %s | out-file %s -encoding utf8";
} else {
format = "adb -s %s shell dumpsys meminfo %s > %s";
}
return format;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package com.microsoft.hydralab.performance.parsers;

import com.google.common.base.Strings;
import com.microsoft.hydralab.performance.Entity.AndroidMemoryInfo;
import com.microsoft.hydralab.performance.PerformanceInspectionResult;
import com.microsoft.hydralab.performance.PerformanceResultParser;
import com.microsoft.hydralab.performance.PerformanceTestResult;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class AndroidMemoryInfoResultParser implements PerformanceResultParser {
private final Logger logger = LoggerFactory.getLogger(getClass());
private static final Map<String, Integer> MEMORY_FILE_TO_DB_INDEX_MAP = new HashMap<>() {
{
put("Java Heap", 0);
put("Native Heap", 1);
put("Code", 2);
put("Stack", 3);
put("Graphics", 4);
put("Private Other", 5);
put("System", 6);
put("Unknown", 7);
}
};

@Override
public PerformanceTestResult parse(PerformanceTestResult performanceTestResult) {
if (performanceTestResult == null || performanceTestResult.performanceInspectionResults == null
|| performanceTestResult.performanceInspectionResults.isEmpty()) {
return null;
}
List<PerformanceInspectionResult> inspectionResults = performanceTestResult.performanceInspectionResults;
for (PerformanceInspectionResult inspectionResult : inspectionResults) {
File logFile = inspectionResult.rawResultFile;
long[] memInfos = parseRawResultFile(logFile);
inspectionResult.parsedData = buildMemoryInfo(inspectionResult.inspection.appId, inspectionResult.inspection.description, inspectionResult.timestamp, memInfos);
}

return performanceTestResult;
}


private AndroidMemoryInfo buildMemoryInfo(String packageName, String description, long timestamp, long[] memInfos) {
if (memInfos == null || memInfos.length != 19) {
return null;
}
AndroidMemoryInfo androidMemoryInfo = new AndroidMemoryInfo();
androidMemoryInfo.setAppPackageName(packageName);
androidMemoryInfo.setDescription(description);
androidMemoryInfo.setTimeStamp(timestamp);
androidMemoryInfo.setJavaHeapPss(memInfos[0]);
androidMemoryInfo.setJavaHeapRss(memInfos[1]);
androidMemoryInfo.setNativeHeapPss(memInfos[2]);
androidMemoryInfo.setNativeHeapRss(memInfos[3]);
androidMemoryInfo.setCodePss(memInfos[4]);
androidMemoryInfo.setCodeRss(memInfos[5]);
androidMemoryInfo.setStackPss(memInfos[6]);
androidMemoryInfo.setStackRss(memInfos[7]);
androidMemoryInfo.setGraphicsPss(memInfos[8]);
androidMemoryInfo.setGraphicsRss(memInfos[9]);
androidMemoryInfo.setPrivateOtherPss(memInfos[10]);
androidMemoryInfo.setPrivateOtherRss(memInfos[11]);
androidMemoryInfo.setSystemPss(memInfos[12]);
androidMemoryInfo.setSystemRss(memInfos[13]);
androidMemoryInfo.setUnknownPss(memInfos[14]);
androidMemoryInfo.setUnknownRss(memInfos[15]);
androidMemoryInfo.setTotalPss(memInfos[16]);
androidMemoryInfo.setTotalRss(memInfos[17]);
androidMemoryInfo.setTotalSwapPss(memInfos[18]);
return androidMemoryInfo;
}

private long[] parseRawResultFile(File rawFile) {
String line;
long[] memoryValueArr = new long[19];
Arrays.fill(memoryValueArr, -1);
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(rawFile))) {
while ((line = bufferedReader.readLine()) != null) {
if (line.trim().startsWith("App Summary")) {
// PSS title line, used to anchor target index offset
line = bufferedReader.readLine();
int pssEndOffset = line.indexOf(")");
int rssEndOffset = line.lastIndexOf(")");

// move to data line
bufferedReader.readLine();
line = bufferedReader.readLine();
while (line != null) {
String lineStr = line.trim();

if (lineStr.startsWith("TOTAL ")) {
if (line.contains("TOTAL PSS:")) {
String pssValue = line.split("TOTAL PSS:")[1].split(" +")[1];
memoryValueArr[16] = NumberUtils.toLong(pssValue, -1);
}
if (line.contains("TOTAL RSS:")) {
String rssValue = line.split("TOTAL RSS:")[1].split(" +")[1];
memoryValueArr[17] = NumberUtils.toLong(rssValue, -1);
}
if (line.contains("TOTAL SWAP PSS:")) {
memoryValueArr[18] = NumberUtils.toLong(line.split("TOTAL SWAP PSS:")[1].split(" +")[1], -1);
}
break;
} else if (!Strings.isNullOrEmpty(lineStr)) {
String[] keyValue = lineStr.split(":");
String key = keyValue[0];
String values = keyValue[1];

// int in map to calculate offset in memoryValueArr array (typeIndex * 2 +0/+1 (implies PSS/RSS correspondingly))
int typeIndex = MEMORY_FILE_TO_DB_INDEX_MAP.get(key);

// for current memory type, PSS data exists
if (line.charAt(pssEndOffset) != ' ') {
memoryValueArr[typeIndex * 2] = NumberUtils.toLong(values.split(" +")[1]);

// for current memory type, RSS data exists
if (line.length() > rssEndOffset && line.charAt(rssEndOffset) != ' ') {
memoryValueArr[typeIndex * 2 + 1] = NumberUtils.toLong(values.split(" +")[2]);
}
} else if (line.length() > rssEndOffset && line.charAt(rssEndOffset) != ' ') {
// for current memory type PSS data doesn't exist and RSS data exists
memoryValueArr[typeIndex * 2 + 1] = NumberUtils.toLong(values.split(" +")[1]);
}
}

line = bufferedReader.readLine();
}
}
}
} catch (IOException e) {
logger.error(e.getMessage(), e);
return null;
}
return memoryValueArr;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.io.Serializable;

import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_ANDROID_BATTERY_INFO;
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_ANDROID_MEMORY_INFO;
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_WIN_BATTERY;
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_WIN_MEMORY;

Expand Down Expand Up @@ -48,6 +49,10 @@ public static PerformanceInspection createWindowsMemoryInspection(String appId,
return createWindowsMemoryInspection(appId, deviceIdentifier, description, false);
}

public static PerformanceInspection createAndroidMemoryInfoInspection(String appId, String deviceIdentifier, String description, boolean isReset) {
return new PerformanceInspection(description, INSPECTOR_ANDROID_MEMORY_INFO, appId, deviceIdentifier, isReset);
}

public static PerformanceInspection createAndroidBatteryInfoInspection(String appId, String deviceIdentifier, String description, boolean isReset) {
return new PerformanceInspection(description, INSPECTOR_ANDROID_BATTERY_INFO, appId, deviceIdentifier, isReset);
}
Expand Down