Skip to content

Commit

Permalink
Add android meminfo inspector and parser (#320)
Browse files Browse the repository at this point in the history
* feat: add android meminfo inspector and parser

* feat: deal with default value
  • Loading branch information
TedaLIEz authored Mar 2, 2023
1 parent c7d4b52 commit faf2459
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 3 deletions.
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

0 comments on commit faf2459

Please sign in to comment.