Skip to content

Commit

Permalink
hprof parser and inspector (#325)
Browse files Browse the repository at this point in the history
* feat: hprof parser

* fix: fix build:

* fix: fix build break

* feat: check dumpheap return value

* feat: check only if app is debuggable

* fix: fix merge issues

* feat: only dump hprof with debugable build

* fix: fix build

* feat: shorten hprof file name

* feat: remove appid in file name

* feat: add final

* feat: remove unused code
  • Loading branch information
TedaLIEz committed Jun 21, 2023
1 parent 1a26a8e commit 889005a
Show file tree
Hide file tree
Showing 17 changed files with 867 additions and 4 deletions.
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

0 comments on commit 889005a

Please sign in to comment.