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

hprof parser and inspector #325

Merged
merged 14 commits into from
Jun 21, 2023
2 changes: 2 additions & 0 deletions common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ dependencies {
compile group: 'net.dongliu', name: 'apk-parser', version: '2.5.3'
//Ipa Parse
compile group: 'com.googlecode.plist', name: 'dd-plist', version: '1.3'

compile 'com.squareup.haha:haha:2.1'
}

repositories {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@
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.AndroidMemoryHprofInspector;
import com.microsoft.hydralab.performance.inspectors.AndroidMemoryHprofInspector;
import com.microsoft.hydralab.performance.inspectors.AndroidMemoryInfoInspector;
import com.microsoft.hydralab.performance.inspectors.IOSEnergyGaugeInspector;
import com.microsoft.hydralab.performance.inspectors.IOSMemoryPerfInspector;
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.AndroidMemoryHprofResultParser;
import com.microsoft.hydralab.performance.parsers.AndroidMemoryHprofResultParser;
import com.microsoft.hydralab.performance.parsers.AndroidMemoryInfoResultParser;
import com.microsoft.hydralab.performance.parsers.IOSEnergyGaugeResultParser;
import com.microsoft.hydralab.performance.parsers.IOSMemoryPerfResultParser;
Expand All @@ -39,13 +43,15 @@
import java.util.concurrent.ScheduledFuture;

import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_ANDROID_BATTERY_INFO;
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_ANDROID_MEMORY_DUMP;
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_ANDROID_MEMORY_INFO;
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_EVENT_TIME;
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_IOS_ENERGY;
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_IOS_MEMORY;
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_WIN_BATTERY;
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_WIN_MEMORY;
import static com.microsoft.hydralab.performance.PerformanceResultParser.PerformanceResultParserType.PARSER_ANDROID_BATTERY_INFO;
import static com.microsoft.hydralab.performance.PerformanceResultParser.PerformanceResultParserType.PARSER_ANDROID_MEMORY_DUMP;
import static com.microsoft.hydralab.performance.PerformanceResultParser.PerformanceResultParserType.PARSER_ANDROID_MEMORY_INFO;
import static com.microsoft.hydralab.performance.PerformanceResultParser.PerformanceResultParserType.PARSER_EVENT_TIME;
import static com.microsoft.hydralab.performance.PerformanceResultParser.PerformanceResultParserType.PARSER_IOS_ENERGY;
Expand All @@ -57,18 +63,20 @@ public class PerformanceTestManagementService implements IPerformanceInspectionS
private static final String PERFORMANCE_FOLDER_NAME = "performance";
private static final Map<PerformanceInspector.PerformanceInspectorType, PerformanceResultParser.PerformanceResultParserType> inspectorParserTypeMap = Map.of(
INSPECTOR_ANDROID_BATTERY_INFO, PARSER_ANDROID_BATTERY_INFO,
INSPECTOR_ANDROID_MEMORY_INFO, PARSER_ANDROID_MEMORY_INFO,
INSPECTOR_WIN_MEMORY, PARSER_WIN_MEMORY,
INSPECTOR_WIN_BATTERY, PARSER_WIN_BATTERY,
INSPECTOR_IOS_ENERGY, PARSER_IOS_ENERGY,
INSPECTOR_IOS_MEMORY, PARSER_IOS_MEMORY,
INSPECTOR_ANDROID_MEMORY_INFO, PARSER_ANDROID_MEMORY_INFO,
INSPECTOR_ANDROID_MEMORY_DUMP, PARSER_ANDROID_MEMORY_DUMP,
INSPECTOR_EVENT_TIME, PARSER_EVENT_TIME
);
private final Map<PerformanceInspector.PerformanceInspectorType, PerformanceInspector> performanceInspectorMap = Map.of(
INSPECTOR_ANDROID_BATTERY_INFO, new AndroidBatteryInfoInspector(),
INSPECTOR_ANDROID_MEMORY_INFO, new AndroidMemoryInfoInspector(),
INSPECTOR_WIN_MEMORY, new WindowsMemoryInspector(),
INSPECTOR_WIN_BATTERY, new WindowsBatteryInspector(),
INSPECTOR_ANDROID_MEMORY_INFO, new AndroidMemoryInfoInspector(),
INSPECTOR_ANDROID_MEMORY_DUMP, new AndroidMemoryHprofInspector(),
INSPECTOR_IOS_MEMORY, new IOSMemoryPerfInspector(),
INSPECTOR_IOS_ENERGY, new IOSEnergyGaugeInspector()
);
Expand All @@ -81,10 +89,11 @@ INSPECTOR_IOS_ENERGY, new IOSEnergyGaugeInspector()
INSPECTOR_IOS_ENERGY, DeviceType.IOS
);
private final Map<PerformanceResultParser.PerformanceResultParserType, PerformanceResultParser> performanceResultParserMap = Map.of(
PARSER_ANDROID_MEMORY_INFO, new AndroidMemoryInfoResultParser(),
PARSER_ANDROID_BATTERY_INFO, new AndroidBatteryInfoResultParser(),
PARSER_WIN_MEMORY, new WindowsMemoryResultParser(),
PARSER_WIN_BATTERY, new WindowsBatteryResultParser(),
PARSER_ANDROID_MEMORY_INFO, new AndroidMemoryInfoResultParser(),
PARSER_ANDROID_MEMORY_DUMP, new AndroidMemoryHprofResultParser(),
PARSER_IOS_ENERGY, new IOSEnergyGaugeResultParser(),
PARSER_IOS_MEMORY, new IOSMemoryPerfResultParser()
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package com.microsoft.hydralab.performance.entity;

import com.microsoft.hydralab.performance.hprof.ObjectInfo;
import lombok.Data;

import java.io.Serializable;
import java.util.List;

@Data
public class AndroidHprofMemoryInfo implements Serializable {
private List<ObjectInfo> bitmapInfoList;
private List<ObjectInfo> topObjectList;
private String appPackageName;
private long timeStamp;
private String description;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
package com.microsoft.hydralab.performance.entity;

import com.alibaba.fastjson.annotation.JSONField;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package com.microsoft.hydralab.performance.hprof;

import java.io.Serializable;

public class BitmapInfo extends ObjectInfo implements Serializable {

public int width;
public int height;
public int density;
public boolean recycled;
public int pixelsCount;
public long nativePtr;
public float perPixelSize;


public void computePerPixelSize() {
perPixelSize = nativeSize * 1f / height / width;
}

@Override
public String getSizeInfo() {
return super.getSizeInfo() + ", &nbsp;BitmapSize: " + width + " &times; " + height;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package com.microsoft.hydralab.performance.hprof;

import com.squareup.haha.perflib.Instance;

public class BitmapInfoExtractor extends Extractor {

@Override
public ObjectInfo extractInstanceInfo(int retainedSizeRanking, Instance instance) {
return extractBitmapInfo(retainedSizeRanking, instance);
}

@Override
public String getType() {
return "bitmap";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package com.microsoft.hydralab.performance.hprof;

import com.squareup.haha.perflib.ClassInstance;
import com.squareup.haha.perflib.ClassObj;
import com.squareup.haha.perflib.Field;
import com.squareup.haha.perflib.Instance;
import com.squareup.haha.perflib.Type;

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

public abstract class Extractor {
/**
* min size in byte
*/
private int minNativeSize = 1000;

protected List<ObjectInfo> resultList = new ArrayList<>();

public void setMinNativeSize(int minNativeSize) {
this.minNativeSize = minNativeSize;
}

public String getName() {
return this.getClass().getSimpleName();
}

void onExtractInfo(int retainedSizeRanking, Instance instance) {
ObjectInfo objectInfo = extractInstanceInfo(retainedSizeRanking, instance);
if (objectInfo == null) {
return;
}
resultList.add(objectInfo);
}

abstract ObjectInfo extractInstanceInfo(int retainedSizeRanking, Instance instance);

public List<ObjectInfo> getResultList() {
return resultList;
}

protected void mapSetBaseAttr(Instance instance, ObjectInfo bitmapInfo) {
List<Instance> founds = new ArrayList<>();
ClassObj classObj = instance.getClassObj();
String className = classObj.getClassName();
String[] names = new String[1];
findRelatedInstances(instance, null, founds, names);
bitmapInfo.instance = instance;
bitmapInfo.firstLevelLauncherRef = founds.size() > 0 ? founds.get(0) : null;
bitmapInfo.className = className;
if (bitmapInfo.firstLevelLauncherRef instanceof ClassObj) {
bitmapInfo.isStaticMember = true;
}
bitmapInfo.fieldName = names[0];
bitmapInfo.nativeSize = instance.getNativeSize();
bitmapInfo.distanceToRoot = instance.getDistanceToGcRoot();
bitmapInfo.retainedSize = instance.getTotalRetainedSize();
bitmapInfo.size = instance.getSize();
bitmapInfo.id = instance.getId();
bitmapInfo.uniqueId = instance.getUniqueId();
}

protected void findRelatedInstances(Instance instance, Instance visited, List<Instance> founds, String[] names) {
if (instance == null) {
return;
}
ClassObj classObj = instance.getClassObj();
if (classObj == null) {
if (!(instance instanceof ClassObj)) {
return;
}
classObj = (ClassObj) instance;
}
String className = classObj.getClassName();
if (className.contains(".launcher")) {
founds.add(instance);
if (instance instanceof ClassInstance) {
ClassInstance classInstance = (ClassInstance) instance;
List<ClassInstance.FieldValue> values = classInstance.getValues();
for (ClassInstance.FieldValue value : values) {
if (Objects.equals(value.getField().getType(), Type.OBJECT)) {
if (Objects.equals(value.getValue(), visited)) {
if (visited != null) {
founds.add(visited);
names[0] = value.getField().getName();
}
}
}
}
// static case
} else if (instance instanceof ClassObj) {
if (instance.getNextInstanceToGcRoot() == null) {
founds.add(instance);
Map<Field, Object> staticFieldValues = ((ClassObj) instance).getStaticFieldValues();
for (Map.Entry<Field, Object> fieldObjectEntry : staticFieldValues.entrySet()) {
Field key = fieldObjectEntry.getKey();
if (Objects.equals(key.getType(), Type.OBJECT)) {
if (Objects.equals(staticFieldValues.get(key), visited)) {
if (visited != null) {
founds.add(visited);
names[0] = key.getName();
}
}
}
}
}
}
return;
}
findRelatedInstances(instance.getNextInstanceToGcRoot(), instance, founds, names);
}

public ObjectInfo extractBitmapInfo(int retainedSizeRanking, Instance instance) {
ClassObj classObj = instance.getClassObj();
String className = classObj.getClassName();
if (className.equals("android.graphics.Bitmap")) {
if (instance instanceof ClassInstance) {
ClassInstance classInstance = (ClassInstance) instance;
List<ClassInstance.FieldValue> values = classInstance.getValues();
BitmapInfo bitmapInfo = new BitmapInfo();
if (instance.getNativeSize() < minNativeSize) {
return null;
}
mapSetBaseAttr(instance, bitmapInfo);
for (ClassInstance.FieldValue value : values) {
Field field = value.getField();
String name = field.getName();
switch (name) {
case "mWidth":
bitmapInfo.width = (int) value.getValue();
break;
case "mHeight":
bitmapInfo.height = (int) value.getValue();
break;
case "mDensity":
bitmapInfo.density = (int) value.getValue();
break;
case "mNativePtr":
bitmapInfo.nativePtr = (long) value.getValue();
break;
case "mRecycled":
bitmapInfo.recycled = (boolean) value.getValue();
break;
}
}
bitmapInfo.computePerPixelSize();
return bitmapInfo;

}
}
return null;
}

public abstract String getType();

public void onExtractComplete() {
resultList.sort((o1, o2) -> Long.compare(o2.retainedSize, o1.retainedSize));
Iterator<ObjectInfo> iterator = resultList.iterator();
int i = 0;
while (iterator.hasNext()) {
ObjectInfo next = iterator.next();
if (next.distanceToRoot == 0 || next.getFieldChainString() == null) {
iterator.remove();
continue;
}
next.index = i + 1;
i++;
}
}
}
Loading