Skip to content

Commit 889005a

Browse files
authored
hprof parser and inspector (#325)
* 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
1 parent 1a26a8e commit 889005a

17 files changed

+867
-4
lines changed

common/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ dependencies {
7272
compile group: 'net.dongliu', name: 'apk-parser', version: '2.5.3'
7373
//Ipa Parse
7474
compile group: 'com.googlecode.plist', name: 'dd-plist', version: '1.3'
75+
76+
compile 'com.squareup.haha:haha:2.1'
7577
}
7678

7779
repositories {

common/src/main/java/com/microsoft/hydralab/performance/PerformanceTestManagementService.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,16 @@
1313
import com.microsoft.hydralab.common.util.FileUtil;
1414
import com.microsoft.hydralab.common.util.ThreadPoolUtil;
1515
import com.microsoft.hydralab.performance.inspectors.AndroidBatteryInfoInspector;
16+
import com.microsoft.hydralab.performance.inspectors.AndroidMemoryHprofInspector;
17+
import com.microsoft.hydralab.performance.inspectors.AndroidMemoryHprofInspector;
1618
import com.microsoft.hydralab.performance.inspectors.AndroidMemoryInfoInspector;
1719
import com.microsoft.hydralab.performance.inspectors.IOSEnergyGaugeInspector;
1820
import com.microsoft.hydralab.performance.inspectors.IOSMemoryPerfInspector;
1921
import com.microsoft.hydralab.performance.inspectors.WindowsBatteryInspector;
2022
import com.microsoft.hydralab.performance.inspectors.WindowsMemoryInspector;
2123
import com.microsoft.hydralab.performance.parsers.AndroidBatteryInfoResultParser;
24+
import com.microsoft.hydralab.performance.parsers.AndroidMemoryHprofResultParser;
25+
import com.microsoft.hydralab.performance.parsers.AndroidMemoryHprofResultParser;
2226
import com.microsoft.hydralab.performance.parsers.AndroidMemoryInfoResultParser;
2327
import com.microsoft.hydralab.performance.parsers.IOSEnergyGaugeResultParser;
2428
import com.microsoft.hydralab.performance.parsers.IOSMemoryPerfResultParser;
@@ -39,13 +43,15 @@
3943
import java.util.concurrent.ScheduledFuture;
4044

4145
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_ANDROID_BATTERY_INFO;
46+
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_ANDROID_MEMORY_DUMP;
4247
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_ANDROID_MEMORY_INFO;
4348
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_EVENT_TIME;
4449
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_IOS_ENERGY;
4550
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_IOS_MEMORY;
4651
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_WIN_BATTERY;
4752
import static com.microsoft.hydralab.performance.PerformanceInspector.PerformanceInspectorType.INSPECTOR_WIN_MEMORY;
4853
import static com.microsoft.hydralab.performance.PerformanceResultParser.PerformanceResultParserType.PARSER_ANDROID_BATTERY_INFO;
54+
import static com.microsoft.hydralab.performance.PerformanceResultParser.PerformanceResultParserType.PARSER_ANDROID_MEMORY_DUMP;
4955
import static com.microsoft.hydralab.performance.PerformanceResultParser.PerformanceResultParserType.PARSER_ANDROID_MEMORY_INFO;
5056
import static com.microsoft.hydralab.performance.PerformanceResultParser.PerformanceResultParserType.PARSER_EVENT_TIME;
5157
import static com.microsoft.hydralab.performance.PerformanceResultParser.PerformanceResultParserType.PARSER_IOS_ENERGY;
@@ -57,18 +63,20 @@ public class PerformanceTestManagementService implements IPerformanceInspectionS
5763
private static final String PERFORMANCE_FOLDER_NAME = "performance";
5864
private static final Map<PerformanceInspector.PerformanceInspectorType, PerformanceResultParser.PerformanceResultParserType> inspectorParserTypeMap = Map.of(
5965
INSPECTOR_ANDROID_BATTERY_INFO, PARSER_ANDROID_BATTERY_INFO,
60-
INSPECTOR_ANDROID_MEMORY_INFO, PARSER_ANDROID_MEMORY_INFO,
6166
INSPECTOR_WIN_MEMORY, PARSER_WIN_MEMORY,
6267
INSPECTOR_WIN_BATTERY, PARSER_WIN_BATTERY,
6368
INSPECTOR_IOS_ENERGY, PARSER_IOS_ENERGY,
6469
INSPECTOR_IOS_MEMORY, PARSER_IOS_MEMORY,
70+
INSPECTOR_ANDROID_MEMORY_INFO, PARSER_ANDROID_MEMORY_INFO,
71+
INSPECTOR_ANDROID_MEMORY_DUMP, PARSER_ANDROID_MEMORY_DUMP,
6572
INSPECTOR_EVENT_TIME, PARSER_EVENT_TIME
6673
);
6774
private final Map<PerformanceInspector.PerformanceInspectorType, PerformanceInspector> performanceInspectorMap = Map.of(
6875
INSPECTOR_ANDROID_BATTERY_INFO, new AndroidBatteryInfoInspector(),
69-
INSPECTOR_ANDROID_MEMORY_INFO, new AndroidMemoryInfoInspector(),
7076
INSPECTOR_WIN_MEMORY, new WindowsMemoryInspector(),
7177
INSPECTOR_WIN_BATTERY, new WindowsBatteryInspector(),
78+
INSPECTOR_ANDROID_MEMORY_INFO, new AndroidMemoryInfoInspector(),
79+
INSPECTOR_ANDROID_MEMORY_DUMP, new AndroidMemoryHprofInspector(),
7280
INSPECTOR_IOS_MEMORY, new IOSMemoryPerfInspector(),
7381
INSPECTOR_IOS_ENERGY, new IOSEnergyGaugeInspector()
7482
);
@@ -81,10 +89,11 @@ INSPECTOR_IOS_ENERGY, new IOSEnergyGaugeInspector()
8189
INSPECTOR_IOS_ENERGY, DeviceType.IOS
8290
);
8391
private final Map<PerformanceResultParser.PerformanceResultParserType, PerformanceResultParser> performanceResultParserMap = Map.of(
84-
PARSER_ANDROID_MEMORY_INFO, new AndroidMemoryInfoResultParser(),
8592
PARSER_ANDROID_BATTERY_INFO, new AndroidBatteryInfoResultParser(),
8693
PARSER_WIN_MEMORY, new WindowsMemoryResultParser(),
8794
PARSER_WIN_BATTERY, new WindowsBatteryResultParser(),
95+
PARSER_ANDROID_MEMORY_INFO, new AndroidMemoryInfoResultParser(),
96+
PARSER_ANDROID_MEMORY_DUMP, new AndroidMemoryHprofResultParser(),
8897
PARSER_IOS_ENERGY, new IOSEnergyGaugeResultParser(),
8998
PARSER_IOS_MEMORY, new IOSMemoryPerfResultParser()
9099
);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.hydralab.performance.entity;
5+
6+
import com.microsoft.hydralab.performance.hprof.ObjectInfo;
7+
import lombok.Data;
8+
9+
import java.io.Serializable;
10+
import java.util.List;
11+
12+
@Data
13+
public class AndroidHprofMemoryInfo implements Serializable {
14+
private List<ObjectInfo> bitmapInfoList;
15+
private List<ObjectInfo> topObjectList;
16+
private String appPackageName;
17+
private long timeStamp;
18+
private String description;
19+
}

common/src/main/java/com/microsoft/hydralab/performance/entity/AndroidMemoryInfo.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
13
package com.microsoft.hydralab.performance.entity;
24

35
import com.alibaba.fastjson.annotation.JSONField;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.hydralab.performance.hprof;
5+
6+
import java.io.Serializable;
7+
8+
public class BitmapInfo extends ObjectInfo implements Serializable {
9+
10+
public int width;
11+
public int height;
12+
public int density;
13+
public boolean recycled;
14+
public int pixelsCount;
15+
public long nativePtr;
16+
public float perPixelSize;
17+
18+
19+
public void computePerPixelSize() {
20+
perPixelSize = nativeSize * 1f / height / width;
21+
}
22+
23+
@Override
24+
public String getSizeInfo() {
25+
return super.getSizeInfo() + ", &nbsp;BitmapSize: " + width + " &times; " + height;
26+
}
27+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.hydralab.performance.hprof;
5+
6+
import com.squareup.haha.perflib.Instance;
7+
8+
public class BitmapInfoExtractor extends Extractor {
9+
10+
@Override
11+
public ObjectInfo extractInstanceInfo(int retainedSizeRanking, Instance instance) {
12+
return extractBitmapInfo(retainedSizeRanking, instance);
13+
}
14+
15+
@Override
16+
public String getType() {
17+
return "bitmap";
18+
}
19+
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
package com.microsoft.hydralab.performance.hprof;
5+
6+
import com.squareup.haha.perflib.ClassInstance;
7+
import com.squareup.haha.perflib.ClassObj;
8+
import com.squareup.haha.perflib.Field;
9+
import com.squareup.haha.perflib.Instance;
10+
import com.squareup.haha.perflib.Type;
11+
12+
import java.util.ArrayList;
13+
import java.util.Iterator;
14+
import java.util.List;
15+
import java.util.Map;
16+
import java.util.Objects;
17+
18+
public abstract class Extractor {
19+
/**
20+
* min size in byte
21+
*/
22+
private int minNativeSize = 1000;
23+
24+
protected List<ObjectInfo> resultList = new ArrayList<>();
25+
26+
public void setMinNativeSize(int minNativeSize) {
27+
this.minNativeSize = minNativeSize;
28+
}
29+
30+
public String getName() {
31+
return this.getClass().getSimpleName();
32+
}
33+
34+
void onExtractInfo(int retainedSizeRanking, Instance instance) {
35+
ObjectInfo objectInfo = extractInstanceInfo(retainedSizeRanking, instance);
36+
if (objectInfo == null) {
37+
return;
38+
}
39+
resultList.add(objectInfo);
40+
}
41+
42+
abstract ObjectInfo extractInstanceInfo(int retainedSizeRanking, Instance instance);
43+
44+
public List<ObjectInfo> getResultList() {
45+
return resultList;
46+
}
47+
48+
protected void mapSetBaseAttr(Instance instance, ObjectInfo bitmapInfo) {
49+
List<Instance> founds = new ArrayList<>();
50+
ClassObj classObj = instance.getClassObj();
51+
String className = classObj.getClassName();
52+
String[] names = new String[1];
53+
findRelatedInstances(instance, null, founds, names);
54+
bitmapInfo.instance = instance;
55+
bitmapInfo.firstLevelLauncherRef = founds.size() > 0 ? founds.get(0) : null;
56+
bitmapInfo.className = className;
57+
if (bitmapInfo.firstLevelLauncherRef instanceof ClassObj) {
58+
bitmapInfo.isStaticMember = true;
59+
}
60+
bitmapInfo.fieldName = names[0];
61+
bitmapInfo.nativeSize = instance.getNativeSize();
62+
bitmapInfo.distanceToRoot = instance.getDistanceToGcRoot();
63+
bitmapInfo.retainedSize = instance.getTotalRetainedSize();
64+
bitmapInfo.size = instance.getSize();
65+
bitmapInfo.id = instance.getId();
66+
bitmapInfo.uniqueId = instance.getUniqueId();
67+
}
68+
69+
protected void findRelatedInstances(Instance instance, Instance visited, List<Instance> founds, String[] names) {
70+
if (instance == null) {
71+
return;
72+
}
73+
ClassObj classObj = instance.getClassObj();
74+
if (classObj == null) {
75+
if (!(instance instanceof ClassObj)) {
76+
return;
77+
}
78+
classObj = (ClassObj) instance;
79+
}
80+
String className = classObj.getClassName();
81+
if (className.contains(".launcher")) {
82+
founds.add(instance);
83+
if (instance instanceof ClassInstance) {
84+
ClassInstance classInstance = (ClassInstance) instance;
85+
List<ClassInstance.FieldValue> values = classInstance.getValues();
86+
for (ClassInstance.FieldValue value : values) {
87+
if (Objects.equals(value.getField().getType(), Type.OBJECT)) {
88+
if (Objects.equals(value.getValue(), visited)) {
89+
if (visited != null) {
90+
founds.add(visited);
91+
names[0] = value.getField().getName();
92+
}
93+
}
94+
}
95+
}
96+
// static case
97+
} else if (instance instanceof ClassObj) {
98+
if (instance.getNextInstanceToGcRoot() == null) {
99+
founds.add(instance);
100+
Map<Field, Object> staticFieldValues = ((ClassObj) instance).getStaticFieldValues();
101+
for (Map.Entry<Field, Object> fieldObjectEntry : staticFieldValues.entrySet()) {
102+
Field key = fieldObjectEntry.getKey();
103+
if (Objects.equals(key.getType(), Type.OBJECT)) {
104+
if (Objects.equals(staticFieldValues.get(key), visited)) {
105+
if (visited != null) {
106+
founds.add(visited);
107+
names[0] = key.getName();
108+
}
109+
}
110+
}
111+
}
112+
}
113+
}
114+
return;
115+
}
116+
findRelatedInstances(instance.getNextInstanceToGcRoot(), instance, founds, names);
117+
}
118+
119+
public ObjectInfo extractBitmapInfo(int retainedSizeRanking, Instance instance) {
120+
ClassObj classObj = instance.getClassObj();
121+
String className = classObj.getClassName();
122+
if (className.equals("android.graphics.Bitmap")) {
123+
if (instance instanceof ClassInstance) {
124+
ClassInstance classInstance = (ClassInstance) instance;
125+
List<ClassInstance.FieldValue> values = classInstance.getValues();
126+
BitmapInfo bitmapInfo = new BitmapInfo();
127+
if (instance.getNativeSize() < minNativeSize) {
128+
return null;
129+
}
130+
mapSetBaseAttr(instance, bitmapInfo);
131+
for (ClassInstance.FieldValue value : values) {
132+
Field field = value.getField();
133+
String name = field.getName();
134+
switch (name) {
135+
case "mWidth":
136+
bitmapInfo.width = (int) value.getValue();
137+
break;
138+
case "mHeight":
139+
bitmapInfo.height = (int) value.getValue();
140+
break;
141+
case "mDensity":
142+
bitmapInfo.density = (int) value.getValue();
143+
break;
144+
case "mNativePtr":
145+
bitmapInfo.nativePtr = (long) value.getValue();
146+
break;
147+
case "mRecycled":
148+
bitmapInfo.recycled = (boolean) value.getValue();
149+
break;
150+
}
151+
}
152+
bitmapInfo.computePerPixelSize();
153+
return bitmapInfo;
154+
155+
}
156+
}
157+
return null;
158+
}
159+
160+
public abstract String getType();
161+
162+
public void onExtractComplete() {
163+
resultList.sort((o1, o2) -> Long.compare(o2.retainedSize, o1.retainedSize));
164+
Iterator<ObjectInfo> iterator = resultList.iterator();
165+
int i = 0;
166+
while (iterator.hasNext()) {
167+
ObjectInfo next = iterator.next();
168+
if (next.distanceToRoot == 0 || next.getFieldChainString() == null) {
169+
iterator.remove();
170+
continue;
171+
}
172+
next.index = i + 1;
173+
i++;
174+
}
175+
}
176+
}

0 commit comments

Comments
 (0)