1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package libcore.heapmetrics;
18 
19 import com.android.ahat.heapdump.AhatHeap;
20 import com.android.ahat.heapdump.AhatInstance;
21 import com.android.ahat.heapdump.AhatSnapshot;
22 import com.android.ahat.heapdump.Size;
23 import com.android.tradefed.device.DeviceNotAvailableException;
24 import com.android.tradefed.device.ITestDevice;
25 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
26 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
27 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
28 import com.android.tradefed.testtype.IDeviceTest;
29 
30 import org.junit.Before;
31 import org.junit.Rule;
32 import org.junit.Test;
33 import org.junit.runner.RunWith;
34 
35 import java.util.EnumMap;
36 import java.util.Map;
37 import libcore.heapmetrics.HeapCategorization.HeapCategory;
38 
39 /**
40  * Tests that gather metrics about zygote+image heap and about the impact of core library calls on
41  * app heap.
42  */
43 @RunWith(DeviceJUnit4ClassRunner.class)
44 public class LibcoreHeapMetricsTest implements IDeviceTest {
45 
46     @Rule public TestMetrics metrics = new TestMetrics();
47     @Rule public TestLogData logs = new TestLogData();
48 
49     private ITestDevice testDevice;
50     private MetricsRunner metricsRunner;
51 
52     @Override
setDevice(ITestDevice device)53     public void setDevice(ITestDevice device) {
54         testDevice = device;
55     }
56 
57     @Override
getDevice()58     public ITestDevice getDevice() {
59         return testDevice;
60     }
61 
62     @Before
initializeHeapDumperRunner()63     public void initializeHeapDumperRunner() throws DeviceNotAvailableException {
64         metricsRunner = MetricsRunner.create(testDevice, logs);
65     }
66 
67     @Test
measureNoop()68     public void measureNoop() throws Exception {
69         MetricsRunner.Result result = metricsRunner.runAllInstrumentations("NOOP");
70         AhatSnapshot beforeDump = result.getBeforeDump();
71         AhatSnapshot afterDump = result.getAfterDump();
72         recordHeapMetrics(beforeDump, "zygoteSize", "zygote");
73         recordHeapMetrics(beforeDump, "imageSize", "image");
74         Map<HeapCategory, Size> zygoteAndImageSizesByCategory = HeapCategorization
75                 .of(beforeDump, beforeDump.getHeap("zygote"), beforeDump.getHeap("image"))
76                 .sizesByCategory();
77         for (Map.Entry<HeapCategory, Size> entry : zygoteAndImageSizesByCategory.entrySet()) {
78             recordSizeMetric(entry.getKey().metricName("zygoteAndImage_"), entry.getValue());
79         }
80         recordBeforeAndAfterAppHeapMetrics(beforeDump, afterDump);
81         recordBytesMetric("beforeTotalPss", result.getBeforeTotalPssKb() * 1024L);
82         recordBytesMetric(
83                 "deltaTotalPss",
84                 (result.getAfterTotalPssKb() - result.getBeforeTotalPssKb()) * 1024L);
85     }
86 
87     @Test
measureCollatorRootLocale()88     public void measureCollatorRootLocale() throws Exception {
89         MetricsRunner.Result result = metricsRunner.runAllInstrumentations("COLLATOR_ROOT_LOCALE");
90         recordBeforeAndAfterAppHeapMetrics(result.getBeforeDump(), result.getAfterDump());
91     }
92 
93     @Test
measureCollatorEnUsLocale()94     public void measureCollatorEnUsLocale() throws Exception {
95         MetricsRunner.Result result = metricsRunner.runAllInstrumentations("COLLATOR_EN_US_LOCALE");
96         recordBeforeAndAfterAppHeapMetrics(result.getBeforeDump(), result.getAfterDump());
97     }
98 
99     @Test
measureCollatorKoreanLocale()100     public void measureCollatorKoreanLocale() throws Exception {
101         MetricsRunner.Result result =
102                 metricsRunner.runAllInstrumentations("COLLATOR_KOREAN_LOCALE");
103         recordBeforeAndAfterAppHeapMetrics(result.getBeforeDump(), result.getAfterDump());
104     }
105 
106     @Test
measureRegexes()107     public void measureRegexes() throws Exception {
108         MetricsRunner.Result result = metricsRunner.runAllInstrumentations("REGEX");
109         recordBeforeAndAfterAppHeapMetrics(result.getBeforeDump(), result.getAfterDump());
110     }
111 
recordHeapMetrics(AhatSnapshot dump, String metricPrefix, String heapName)112     private void recordHeapMetrics(AhatSnapshot dump, String metricPrefix, String heapName) {
113         AhatHeap heap = dump.getHeap(heapName);
114         recordSizeMetric(metricPrefix, heap.getSize());
115         Map<Reachability, Size> sizesByReachability = sizesByReachability(dump, heap);
116         for (Reachability reachability : Reachability.values()) {
117             recordSizeMetric(
118                     reachability.metricName(metricPrefix), sizesByReachability.get(reachability));
119         }
120     }
121 
recordBeforeAndAfterAppHeapMetrics( AhatSnapshot beforeDump, AhatSnapshot afterDump)122     private void recordBeforeAndAfterAppHeapMetrics(
123             AhatSnapshot beforeDump,
124             AhatSnapshot afterDump) {
125         AhatHeap beforeHeap = beforeDump.getHeap("app");
126         AhatHeap afterHeap = afterDump.getHeap("app");
127         recordSizeMetric("beforeAppSize", beforeHeap.getSize());
128         recordSizeDeltaMetric("deltaAppSize", beforeHeap.getSize(), afterHeap.getSize());
129         Map<Reachability, Size> beforeSizesByReachability =
130                 sizesByReachability(beforeDump, beforeHeap);
131         Map<Reachability, Size> afterSizesByReachability = sizesByReachability(afterDump, afterHeap);
132         for (Reachability reachability : Reachability.values()) {
133             recordSizeMetric(
134                     reachability.metricName("beforeAppSize"),
135                     beforeSizesByReachability.get(reachability));
136             recordSizeDeltaMetric(
137                     reachability.metricName("deltaAppSize"),
138                     beforeSizesByReachability.get(reachability),
139                     afterSizesByReachability.get(reachability));
140         }
141     }
142 
recordSizeMetric(String name, Size size)143     private void recordSizeMetric(String name, Size size) {
144         recordBytesMetric(name, size.getSize());
145     }
146 
recordSizeDeltaMetric(String name, Size before, Size after)147     private void recordSizeDeltaMetric(String name, Size before, Size after) {
148         recordBytesMetric(name, after.getSize() - before.getSize());
149     }
150 
recordBytesMetric(String name, long bytes)151     private void recordBytesMetric(String name, long bytes) {
152         metrics.addTestMetric(name, Long.toString(bytes));
153     }
154 
sizesByReachability(AhatSnapshot dump, AhatHeap heap)155     private static Map<Reachability, Size> sizesByReachability(AhatSnapshot dump, AhatHeap heap) {
156         EnumMap<Reachability, Size> map = new EnumMap<>(Reachability.class);
157         for (Reachability reachability : Reachability.values()) {
158             map.put(reachability, Size.ZERO);
159         }
160         for (AhatInstance instance : dump.getRooted()) {
161             Reachability reachability = Reachability.ofInstance(instance);
162             Size size = instance.getRetainedSize(heap);
163             map.put(reachability, map.get(reachability).plus(size));
164         }
165         return map;
166     }
167 }
168