1 /*
2  * Copyright (C) 2018 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 com.android.tests.sysmem.host;
18 
19 import com.android.tradefed.result.FileInputStreamSource;
20 import com.android.tradefed.result.LogDataType;
21 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
22 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
23 
24 import java.io.File;
25 import java.io.IOException;
26 import java.io.PrintStream;
27 import java.util.InputMismatchException;
28 import java.util.Scanner;
29 
30 /**
31  * Utilities for sampling and reporting memory metrics.
32  */
33 class Metrics {
34 
35     private Device mDevice;
36     private TestMetrics mMetrics;
37     private TestLogData mLogs;
38 
39     /**
40      * Constructs a metrics instance that will output high level metrics and
41      * more detailed breakdowns using the given <code>metrics</code> and
42      * <code>logs</code> objects.
43      *
44      * @param device the device to sample metrics from
45      * @param metrics where to log the high level metrics when taking a sample
46      * @param logs where to log detailed breakdowns when taking a sample
47      */
Metrics(Device device, TestMetrics metrics, TestLogData logs)48     Metrics(Device device, TestMetrics metrics, TestLogData logs) {
49         this.mDevice = device;
50         this.mMetrics = metrics;
51         this.mLogs = logs;
52     }
53 
54     /**
55      * Writes the given <code>text</code> to a log with the given label.
56      */
logText(String label, String text)57     private void logText(String label, String text) throws TestException {
58         try {
59             File file = File.createTempFile(label, "txt");
60             PrintStream ps = new PrintStream(file);
61             ps.print(text);
62             try (FileInputStreamSource dataStream = new FileInputStreamSource(file)) {
63                 mLogs.addTestLog(label, LogDataType.TEXT, dataStream);
64             }
65         } catch (IOException e) {
66             throw new TestException(e);
67         }
68     }
69 
70     /**
71      * Samples the current memory use on the system. Outputs high level test
72      * metrics and detailed breakdowns to the TestMetrics and TestLogData
73      * objects provided when constructing this Metrics instance. The metrics
74      * and log names are prefixed with the given label.
75      *
76      * @param label prefix to use for metrics and logs output for this sample.
77      */
sample(String label)78     void sample(String label) throws TestException {
79         // adb root access is required to get showmap
80         mDevice.enableAdbRoot();
81 
82         int pid = mDevice.getProcessPid("system_server");
83 
84         // Read showmap for system server and add it as a test log
85         String showmap = mDevice.executeShellCommand("showmap " + pid);
86         logText(label + ".system_server.showmap", showmap);
87 
88         // Extract VSS, PSS and RSS from the showmap and output them as metrics.
89         // The last lines of the showmap output looks something like:
90         // CHECKSTYLE:OFF Generated code
91         // virtual                     shared   shared  private  private
92         //    size      RSS      PSS    clean    dirty    clean    dirty     swap  swapPSS   # object
93         //-------- -------- -------- -------- -------- -------- -------- -------- -------- ---- ------------------------------
94         //  928480   113016    24860    87348     7916     3632    14120     1968     1968 1900 TOTAL
95         // CHECKSTYLE:ON Generated code
96         try {
97             int pos = showmap.lastIndexOf("----");
98             Scanner sc = new Scanner(showmap.substring(pos));
99             sc.next();
100             long vss = sc.nextLong();
101             long rss = sc.nextLong();
102             long pss = sc.nextLong();
103 
104             mMetrics.addTestMetric(label + ".system_server.vss", Long.toString(vss));
105             mMetrics.addTestMetric(label + ".system_server.rss", Long.toString(rss));
106             mMetrics.addTestMetric(label + ".system_server.pss", Long.toString(pss));
107         } catch (InputMismatchException e) {
108             throw new TestException("unexpected showmap format", e);
109         }
110 
111         // Run debuggerd -j to get GC stats for system server and add it as a
112         // test log
113         String debuggerd = mDevice.executeShellCommand("debuggerd -j " + pid);
114         logText(label + ".system_server.debuggerd", debuggerd);
115 
116         // TODO: Experiment with other additional metrics.
117 
118         // TODO: Consider launching an instrumentation to collect metrics from
119         // within the device itself.
120     }
121 }
122