| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.performance.tests; |
| |
| import com.android.tradefed.device.DeviceNotAvailableException; |
| import com.android.tradefed.device.ITestDevice; |
| import com.android.tradefed.log.LogUtil.CLog; |
| import com.android.tradefed.result.ITestInvocationListener; |
| import com.android.tradefed.result.InputStreamSource; |
| import com.android.tradefed.result.LogDataType; |
| import com.android.tradefed.testtype.IDeviceTest; |
| import com.android.tradefed.testtype.IRemoteTest; |
| import com.android.tradefed.util.brillopad.BugreportParser; |
| import com.android.tradefed.util.brillopad.ItemList; |
| import com.android.tradefed.util.brillopad.item.GenericMapItem; |
| import com.android.tradefed.util.brillopad.item.IItem; |
| import com.android.tradefed.util.brillopad.section.MemInfoParser; |
| import com.android.tradefed.util.brillopad.section.ProcRankParser; |
| |
| import java.io.IOException; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import junit.framework.Assert; |
| |
| /** |
| * Tests to gather device metrics from during and immediately after boot |
| */ |
| public class StartupMetricsTest implements IDeviceTest, IRemoteTest { |
| public static final String BUGREPORT_LOG_NAME = "bugreport_startup.txt"; |
| |
| ITestDevice mTestDevice = null; |
| |
| @Override |
| public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { |
| Assert.assertNotNull(mTestDevice); |
| |
| executeRebootTest(listener); |
| fetchBugReportMetrics(listener); |
| // TODO: pass build metrics? does it make sense to have it here? |
| } |
| |
| /** |
| * Check how long the device takes to come online and become available after |
| * a reboot |
| * |
| * @param listener the {@link ITestInvocationListener} of test results |
| */ |
| void executeRebootTest(ITestInvocationListener listener) throws DeviceNotAvailableException { |
| Map<String, String> runMetrics = new HashMap<String, String>(); |
| |
| long startTime = System.currentTimeMillis(); |
| mTestDevice.rebootUntilOnline(); |
| long onlineTime = System.currentTimeMillis(); |
| mTestDevice.waitForDeviceAvailable(); |
| long availableTime = System.currentTimeMillis(); |
| |
| long offlineDuration = onlineTime - startTime; |
| long unavailDuration = availableTime - startTime; |
| CLog.d("Reboot: %d millis until online, %d until available", |
| offlineDuration, unavailDuration); |
| runMetrics.put("online", Long.toString(offlineDuration)); |
| runMetrics.put("bootcomplete", Long.toString(unavailDuration)); |
| |
| reportMetrics(listener, "boottime", runMetrics); |
| } |
| |
| /** |
| * Fetch proc rank metrics from the bugreport after reboot |
| * |
| * @param listener the {@link ITestInvocationListener} of test results |
| */ |
| @SuppressWarnings("unchecked") |
| void fetchBugReportMetrics(ITestInvocationListener listener) { |
| BugreportParser parser = new BugreportParser(); |
| ItemList bugreport = null; |
| // Retrieve bugreport |
| InputStreamSource bugSource = mTestDevice.getBugreport(); |
| try { |
| listener.testLog(BUGREPORT_LOG_NAME, LogDataType.TEXT, bugSource); |
| bugreport = parser.parse(bugSource); |
| } catch (IOException e) { |
| Assert.fail(String.format("Failed to fetch and parse bugreport for device %s: %s", |
| mTestDevice.getSerialNumber(), e)); |
| } finally { |
| bugSource.cancel(); |
| } |
| // Process meminfo information and post it to the dashboard |
| IItem item = bugreport.getFirstItemByType(MemInfoParser.SECTION_NAME); |
| if (item != null) { |
| Map<String, String> memInfoMap = convertMap((GenericMapItem<String, Integer>) item); |
| reportMetrics(listener, "startup-meminfo", memInfoMap); |
| } |
| |
| // Process procrank information and post it to the dashboard |
| item = bugreport.getFirstItemByType(ProcRankParser.SECTION_NAME); |
| if (item != null) { |
| Map <String, Map<String, Integer>> procRankMap = |
| (GenericMapItem<String, Map<String, Integer>>) item; |
| parseProcRankMap(listener, procRankMap); |
| } |
| } |
| |
| /** |
| * Helper method to convert Map<String, Integer> to Map<String, String> |
| * |
| * @param input the {@link Map} to convert from |
| * @return output the converted {@link Map} |
| */ |
| Map<String, String> convertMap(Map<String, Integer> input) { |
| Map<String, String> output = new HashMap<String, String>(); |
| for (Map.Entry<String, Integer> entry : input.entrySet()) { |
| output.put(entry.getKey(), entry.getValue().toString()); |
| } |
| return output; |
| } |
| |
| /** |
| * Report run metrics by creating an empty test run to stick them in |
| * <p /> |
| * Exposed for unit testing |
| * |
| * @param listener the {@link ITestInvocationListener} of test results |
| * @param runName the test name |
| * @param metrics the {@link Map} that contains metrics for the given test |
| */ |
| void reportMetrics(ITestInvocationListener listener, String runName, |
| Map<String, String> metrics) { |
| // Create an empty testRun to report the parsed runMetrics |
| CLog.d("About to report metrics: %s", metrics); |
| listener.testRunStarted(runName, 0); |
| listener.testRunEnded(0, metrics); |
| } |
| |
| /** |
| * Aggregates the procrank data by the pss, rss, and uss values. |
| * |
| * @param listener the {@link ITestInvocationListener} of test results |
| * @param procRankMap the {@link Map} parsed from brillopad for the procrank |
| * section |
| */ |
| void parseProcRankMap(ITestInvocationListener listener, |
| Map<String, Map<String, Integer>> procRankMap) { |
| // final maps for pss, rss, and uss. |
| Map<String, String> pssOutput = new HashMap<String, String>(); |
| Map<String, String> rssOutput = new HashMap<String, String>(); |
| Map<String, String> ussOutput = new HashMap<String, String>(); |
| // total number of processes. |
| Integer numProcess = 0; |
| // aggregate pss, rss, uss across all processes. |
| Integer pssTotal = 0; |
| Integer rssTotal = 0; |
| Integer ussTotal = 0; |
| |
| for (Map.Entry<String, Map<String, Integer>> entry : procRankMap.entrySet()) { |
| // Skip empty processes. |
| if (entry.getKey() == null) |
| continue; |
| if (entry.getKey().length() == 0) |
| continue; |
| |
| numProcess++; |
| Map<String, Integer> valueMap = entry.getValue(); |
| Integer pss = valueMap.get("Pss"); |
| Integer rss = valueMap.get("Rss"); |
| Integer uss = valueMap.get("Uss"); |
| if (pss != null) { |
| pssTotal += pss; |
| pssOutput.put(entry.getKey(), pss.toString()); |
| } |
| if (rss != null) { |
| rssTotal += rss; |
| rssOutput.put(entry.getKey(), rss.toString()); |
| } |
| if (uss != null) { |
| ussTotal += pss; |
| ussOutput.put(entry.getKey(), uss.toString()); |
| } |
| } |
| // Add aggregation data. |
| pssOutput.put("count", numProcess.toString()); |
| pssOutput.put("total", pssTotal.toString()); |
| rssOutput.put("count", numProcess.toString()); |
| rssOutput.put("total", rssTotal.toString()); |
| ussOutput.put("count", numProcess.toString()); |
| ussOutput.put("total", ussTotal.toString()); |
| |
| // Report metrics to dashboard |
| reportMetrics(listener, "startup-procrank-pss", pssOutput); |
| reportMetrics(listener, "startup-procrank-rss", rssOutput); |
| reportMetrics(listener, "startup-procrank-uss", ussOutput); |
| } |
| |
| @Override |
| public void setDevice(ITestDevice device) { |
| mTestDevice = device; |
| } |
| |
| @Override |
| public ITestDevice getDevice() { |
| return mTestDevice; |
| } |
| } |