1 /* 2 * Copyright (C) 2015 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 android.support.test.aupt; 18 19 import android.util.Log; 20 21 import java.lang.StringBuilder; 22 import java.util.List; 23 import java.util.regex.Pattern; 24 import java.util.regex.Matcher; 25 26 /** 27 * This class is like a C-style struct that holds individual process information from the 28 * dumpsys graphicsstats command. It also includes an enumeration, originally from the 29 * JankTestHelper code, which pattern matches against the dump data to find the relevant 30 * information. 31 */ 32 public class JankStat { 33 private static final String TAG = "JankStat"; 34 35 // Patterns used for parsing dumpsys graphicsstats 36 public enum StatPattern { 37 PACKAGE(Pattern.compile("\\s*Package: (.*)"), 1), 38 STATS_SINCE(Pattern.compile("\\s*Stats since: (\\d+)ns"), 1), 39 TOTAL_FRAMES(Pattern.compile("\\s*Total frames rendered: (\\d+)"), 1), 40 NUM_JANKY(Pattern.compile("\\s*Janky frames: (\\d+) (.*)"), 1), 41 FRAME_TIME_50TH(Pattern.compile("\\s*50th percentile: (\\d+)ms"), 1), 42 FRAME_TIME_90TH(Pattern.compile("\\s*90th percentile: (\\d+)ms"), 1), 43 FRAME_TIME_95TH(Pattern.compile("\\s*95th percentile: (\\d+)ms"), 1), 44 FRAME_TIME_99TH(Pattern.compile("\\s*99th percentile: (\\d+)ms"), 1), 45 SLOWEST_FRAMES_24H(Pattern.compile("\\s*Slowest frames over last 24h: (.*)"), 1), 46 NUM_MISSED_VSYNC(Pattern.compile("\\s*Number Missed Vsync: (\\d+)"), 1), 47 NUM_HIGH_INPUT_LATENCY(Pattern.compile("\\s*Number High input latency: (\\d+)"), 1), 48 NUM_SLOW_UI_THREAD(Pattern.compile("\\s*Number Slow UI thread: (\\d+)"), 1), 49 NUM_SLOW_BITMAP_UPLOADS(Pattern.compile("\\s*Number Slow bitmap uploads: (\\d+)"), 1), 50 NUM_SLOW_DRAW(Pattern.compile("\\s*Number Slow issue draw commands: (\\d+)"), 1); 51 52 private Pattern mParsePattern; 53 private int mGroupIdx; 54 StatPattern(Pattern pattern, int idx)55 StatPattern(Pattern pattern, int idx) { 56 mParsePattern = pattern; 57 mGroupIdx = idx; 58 } 59 parse(String line)60 String parse(String line) { 61 String ret = null; 62 Matcher matcher = mParsePattern.matcher(line); 63 if (matcher.matches()) { 64 ret = matcher.group(mGroupIdx); 65 } 66 return ret; 67 } 68 } 69 70 public String packageName; 71 public long statsSince; 72 public int totalFrames; 73 public int jankyFrames; 74 public int frameTime50th; 75 public int frameTime90th; 76 public int frameTime95th; 77 public int frameTime99th; 78 public String slowestFrames24h; 79 public int numMissedVsync; 80 public int numHighLatency; 81 public int numSlowUiThread; 82 public int numSlowBitmap; 83 public int numSlowDraw; 84 85 public int aggregateCount; 86 JankStat(String pkg, long since, int total, int janky, int ft50, int ft90, int ft95, int ft99, String slow24h, int vsync, int latency, int slowUi, int slowBmp, int slowDraw, int aggCount)87 public JankStat (String pkg, long since, int total, int janky, int ft50, int ft90, int ft95, 88 int ft99, String slow24h, int vsync, int latency, int slowUi, int slowBmp, int slowDraw, 89 int aggCount) { 90 packageName = pkg; 91 statsSince = since; 92 totalFrames = total; 93 jankyFrames = janky; 94 frameTime50th = ft50; 95 frameTime90th = ft90; 96 frameTime95th = ft95; 97 frameTime99th = ft99; 98 slowestFrames24h = slow24h; 99 numMissedVsync = vsync; 100 numHighLatency = latency; 101 numSlowUiThread = slowUi; 102 numSlowBitmap = slowBmp; 103 numSlowDraw = slowDraw; 104 105 aggregateCount = aggCount; 106 } 107 108 /** 109 * Determines if this set of janks stats is aggregated from the 110 * previous set of metrics or if they are a new set, meaning the 111 * old process was killed, had its stats reset, and was then 112 * restarted. 113 */ isContinuedFrom(JankStat prevMetrics)114 public boolean isContinuedFrom (JankStat prevMetrics) { 115 return statsSince == prevMetrics.statsSince; 116 } 117 118 /** 119 * Returns the percent of frames that appeared janky 120 */ getPercentJankyFrames()121 public float getPercentJankyFrames () { 122 return jankyFrames / (float)totalFrames; 123 } 124 125 /** 126 * Returns the jank stats similar to how they are presented in the shell 127 */ 128 @Override toString()129 public String toString () { 130 String result = packageName + 131 "\nStats since: " + statsSince + 132 "\nTotal frames: " + totalFrames + 133 "\nJanky frames: " + jankyFrames + 134 "\n50th percentile: " + frameTime50th + 135 "\n90th percentile: " + frameTime90th + 136 "\n95th percentile: " + frameTime95th + 137 "\n99th percentile: " + frameTime99th + 138 "\nSlowest frames over last 24h: " + slowestFrames24h + 139 "\nNumber Missed Vsync: " + numMissedVsync + 140 "\nNumber High input latency: " + numHighLatency + 141 "\nNumber Slow UI thread: " + numSlowUiThread + 142 "\nNumber Slow bitmap uploads: " + numSlowBitmap + 143 "\nNumber Slow draw commands: " + numSlowDraw + 144 "\nAggregated stats count: " + aggregateCount; 145 return result; 146 } 147 148 /** 149 * Merges the stat history of a sequence of stats. 150 * 151 * Final count value = sum of count values across stats 152 * Final ##th percentile = weighted average of ##th, weight by total frames 153 * ## = 90, 95, and 99 154 */ mergeStatHistory(List<JankStat> statHistory)155 public static JankStat mergeStatHistory (List<JankStat> statHistory) { 156 if (statHistory.size() == 0) 157 return null; 158 else if (statHistory.size() == 1) 159 return statHistory.get(0); 160 161 String pkg = statHistory.get(0).packageName; 162 long totalStatsSince = statHistory.get(0).statsSince; 163 int totalTotalFrames = 0; 164 int totalJankyFrames = 0; 165 int totalNumMissedVsync = 0; 166 int totalNumHighLatency = 0; 167 int totalNumSlowUiThread = 0; 168 int totalNumSlowBitmap = 0; 169 int totalNumSlowDraw = 0; 170 String totalSlow24h = ""; 171 172 for (JankStat stat : statHistory) { 173 totalTotalFrames += stat.totalFrames; 174 totalJankyFrames += stat.jankyFrames; 175 totalNumMissedVsync += stat.numMissedVsync; 176 totalNumHighLatency += stat.numHighLatency; 177 totalNumSlowUiThread += stat.numSlowUiThread; 178 totalNumSlowBitmap += stat.numSlowBitmap; 179 totalNumSlowDraw += stat.numSlowDraw; 180 totalSlow24h += stat.slowestFrames24h; 181 } 182 183 float wgtAvgPercentile50 = 0f; 184 float wgtAvgPercentile90 = 0f; 185 float wgtAvgPercentile95 = 0f; 186 float wgtAvgPercentile99 = 0f; 187 for (JankStat stat : statHistory) { 188 float weight = ((float)stat.totalFrames / totalTotalFrames); 189 Log.v(TAG, String.format("Calculated weight is %f", weight)); 190 wgtAvgPercentile90 += stat.frameTime50th * weight; 191 wgtAvgPercentile90 += stat.frameTime90th * weight; 192 wgtAvgPercentile95 += stat.frameTime95th * weight; 193 wgtAvgPercentile99 += stat.frameTime99th * weight; 194 } 195 196 int perc50 = (int)Math.ceil(wgtAvgPercentile50); 197 int perc90 = (int)Math.ceil(wgtAvgPercentile90); 198 int perc95 = (int)Math.ceil(wgtAvgPercentile95); 199 int perc99 = (int)Math.ceil(wgtAvgPercentile99); 200 201 return new JankStat(pkg, totalStatsSince, totalTotalFrames, 202 totalJankyFrames, perc50, perc90, perc95, perc99, totalSlow24h, 203 totalNumMissedVsync, totalNumHighLatency, totalNumSlowUiThread, totalNumSlowBitmap, 204 totalNumSlowDraw, statHistory.size()); 205 } 206 207 /** 208 * Returns a long String containing each JankStat object separated by a 209 * newline. Ideally, this would omit objects with zero rendered total 210 * frames, which is junk data. 211 */ statsListToString(List<JankStat> statsList)212 public static String statsListToString (List<JankStat> statsList) { 213 StringBuilder result = new StringBuilder(); 214 for (JankStat stats : statsList) { 215 result.append(stats.toString()); 216 result.append("\n"); 217 } 218 219 return result.toString(); 220 } 221 } 222