1 /* 2 * Copyright (C) 2023 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.view; 18 19 import static android.Manifest.permission.READ_FRAME_BUFFER; 20 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.RequiresPermission; 25 import android.content.Context; 26 import android.os.SystemClock; 27 import android.util.Log; 28 29 import com.android.internal.annotations.GuardedBy; 30 import com.android.internal.annotations.VisibleForTesting; 31 import com.android.internal.util.GcUtils; 32 33 import java.io.PrintWriter; 34 import java.util.ArrayList; 35 import java.util.Map; 36 import java.util.WeakHashMap; 37 38 /** 39 * A thread-safe registry used to track surface controls that are active (not yet released) within a 40 * process, to help debug and identify leaks. 41 * @hide 42 */ 43 public class SurfaceControlRegistry { 44 private static final String TAG = "SurfaceControlRegistry"; 45 46 /** 47 * An interface for processing the registered SurfaceControls when the threshold is exceeded. 48 */ 49 public interface Reporter { 50 /** 51 * Called when the set of layers exceeds the max threshold. This can be called on any 52 * thread, and must be handled synchronously. 53 */ onMaxLayersExceeded(WeakHashMap<SurfaceControl, Long> surfaceControls, int limit, PrintWriter pw)54 void onMaxLayersExceeded(WeakHashMap<SurfaceControl, Long> surfaceControls, int limit, 55 PrintWriter pw); 56 } 57 58 /** 59 * The default implementation of the reporter which logs the existing registered surfaces to 60 * logcat. 61 */ 62 private static class DefaultReporter implements Reporter { onMaxLayersExceeded(WeakHashMap<SurfaceControl, Long> surfaceControls, int limit, PrintWriter pw)63 public void onMaxLayersExceeded(WeakHashMap<SurfaceControl, Long> surfaceControls, 64 int limit, PrintWriter pw) { 65 final long now = SystemClock.elapsedRealtime(); 66 final ArrayList<Map.Entry<SurfaceControl, Long>> entries = new ArrayList<>(); 67 for (Map.Entry<SurfaceControl, Long> entry : surfaceControls.entrySet()) { 68 entries.add(entry); 69 } 70 // Sort entries by time registered when dumping 71 // TODO: Or should it sort by name? 72 entries.sort((o1, o2) -> (int) (o1.getValue() - o2.getValue())); 73 final int size = Math.min(entries.size(), limit); 74 75 pw.println("SurfaceControlRegistry"); 76 pw.println("----------------------"); 77 pw.println("Listing oldest " + size + " of " + surfaceControls.size()); 78 for (int i = 0; i < size; i++) { 79 final Map.Entry<SurfaceControl, Long> entry = entries.get(i); 80 final SurfaceControl sc = entry.getKey(); 81 final long timeRegistered = entry.getValue(); 82 pw.print(" "); 83 pw.print(sc.getName()); 84 pw.print(" (" + sc.getCallsite() + ")"); 85 pw.println(" [" + ((now - timeRegistered) / 1000) + "s ago]"); 86 } 87 } 88 } 89 90 // The threshold at which to dump information about all the known active SurfaceControls in the 91 // process when the number of layers exceeds a certain count. This should be significantly 92 // smaller than the MAX_LAYERS (currently 4096) defined in SurfaceFlinger.h 93 private static final int MAX_LAYERS_REPORTING_THRESHOLD = 1024; 94 95 // The threshold at which to reset the dump state. Needs to be smaller than 96 // MAX_LAYERS_REPORTING_THRESHOLD 97 private static final int RESET_REPORTING_THRESHOLD = 256; 98 99 // Number of surface controls to dump when the max threshold is exceeded 100 private static final int DUMP_LIMIT = 256; 101 102 // Static lock, must be held for all registry operations 103 private static final Object sLock = new Object(); 104 105 // The default reporter for printing out the registered surfaces 106 private static final DefaultReporter sDefaultReporter = new DefaultReporter(); 107 108 // The registry for a given process 109 private static volatile SurfaceControlRegistry sProcessRegistry; 110 111 // Mapping of the active SurfaceControls to the elapsed time when they were registered 112 @GuardedBy("sLock") 113 private final WeakHashMap<SurfaceControl, Long> mSurfaceControls; 114 115 // The threshold at which we dump information about the current set of registered surfaces. 116 // Once this threshold is reached, we no longer report until the number of layers drops below 117 // mResetReportingThreshold to ensure that we don't spam logcat. 118 private int mMaxLayersReportingThreshold = MAX_LAYERS_REPORTING_THRESHOLD; 119 private int mResetReportingThreshold = RESET_REPORTING_THRESHOLD; 120 121 // Whether the current set of layers has exceeded mMaxLayersReportingThreshold, and we have 122 // already reported the set of registered surfaces. 123 private boolean mHasReportedExceedingMaxThreshold = false; 124 125 // The handler for when the registry exceeds the max threshold 126 private Reporter mReporter = sDefaultReporter; 127 SurfaceControlRegistry()128 private SurfaceControlRegistry() { 129 mSurfaceControls = new WeakHashMap<>(256); 130 } 131 132 /** 133 * Sets the thresholds at which the registry reports errors. 134 * @param maxLayersReportingThreshold The max threshold (inclusive) 135 * @param resetReportingThreshold The reset threshold (inclusive) 136 * @hide 137 */ 138 @VisibleForTesting setReportingThresholds(int maxLayersReportingThreshold, int resetReportingThreshold, Reporter reporter)139 public void setReportingThresholds(int maxLayersReportingThreshold, int resetReportingThreshold, 140 Reporter reporter) { 141 synchronized (sLock) { 142 if (maxLayersReportingThreshold <= 0 143 || resetReportingThreshold >= maxLayersReportingThreshold) { 144 throw new IllegalArgumentException("Expected maxLayersReportingThreshold (" 145 + maxLayersReportingThreshold + ") to be > 0 and resetReportingThreshold (" 146 + resetReportingThreshold + ") to be < maxLayersReportingThreshold"); 147 } 148 if (reporter == null) { 149 throw new IllegalArgumentException("Expected non-null reporter"); 150 } 151 mMaxLayersReportingThreshold = maxLayersReportingThreshold; 152 mResetReportingThreshold = resetReportingThreshold; 153 mHasReportedExceedingMaxThreshold = false; 154 mReporter = reporter; 155 } 156 } 157 158 /** 159 * Creates and initializes the registry for all SurfaceControls in this process. The caller must 160 * hold the READ_FRAME_BUFFER permission. 161 * @hide 162 */ 163 @RequiresPermission(READ_FRAME_BUFFER) 164 @NonNull createProcessInstance(Context context)165 public static void createProcessInstance(Context context) { 166 if (context.checkSelfPermission(READ_FRAME_BUFFER) != PERMISSION_GRANTED) { 167 throw new SecurityException("Expected caller to hold READ_FRAME_BUFFER"); 168 } 169 synchronized (sLock) { 170 if (sProcessRegistry == null) { 171 sProcessRegistry = new SurfaceControlRegistry(); 172 } 173 } 174 } 175 176 /** 177 * Destroys the previously created registry this process. 178 * @hide 179 */ destroyProcessInstance()180 public static void destroyProcessInstance() { 181 synchronized (sLock) { 182 if (sProcessRegistry == null) { 183 return; 184 } 185 sProcessRegistry = null; 186 } 187 } 188 189 /** 190 * Returns the instance of the registry for this process, only non-null if 191 * createProcessInstance(Context) was previously called from a valid caller. 192 * @hide 193 */ 194 @Nullable 195 @VisibleForTesting getProcessInstance()196 public static SurfaceControlRegistry getProcessInstance() { 197 synchronized (sLock) { 198 return sProcessRegistry; 199 } 200 } 201 202 /** 203 * Adds a SurfaceControl to the registry. 204 */ add(SurfaceControl sc)205 void add(SurfaceControl sc) { 206 synchronized (sLock) { 207 mSurfaceControls.put(sc, SystemClock.elapsedRealtime()); 208 if (!mHasReportedExceedingMaxThreshold 209 && mSurfaceControls.size() >= mMaxLayersReportingThreshold) { 210 // Dump existing info to logcat for debugging purposes (but don't close the 211 // System.out output stream otherwise we can't print to it after this call) 212 PrintWriter pw = new PrintWriter(System.out, true /* autoFlush */); 213 mReporter.onMaxLayersExceeded(mSurfaceControls, DUMP_LIMIT, pw); 214 mHasReportedExceedingMaxThreshold = true; 215 } 216 } 217 } 218 219 /** 220 * Removes a SurfaceControl from the registry. 221 */ remove(SurfaceControl sc)222 void remove(SurfaceControl sc) { 223 synchronized (sLock) { 224 mSurfaceControls.remove(sc); 225 if (mHasReportedExceedingMaxThreshold 226 && mSurfaceControls.size() <= mResetReportingThreshold) { 227 mHasReportedExceedingMaxThreshold = false; 228 } 229 } 230 } 231 232 /** 233 * Returns a hash of this registry and is a function of all the active surface controls. This 234 * is useful for testing to determine whether the registry has changed between creating and 235 * destroying new SurfaceControls. 236 */ 237 @Override hashCode()238 public int hashCode() { 239 synchronized (sLock) { 240 // Return a hash of the surface controls 241 return mSurfaceControls.keySet().hashCode(); 242 } 243 } 244 245 /** 246 * Forces the gc and finalizers to run, used prior to dumping to ensure we only dump strongly 247 * referenced surface controls. 248 */ runGcAndFinalizers()249 private static void runGcAndFinalizers() { 250 long t = SystemClock.elapsedRealtime(); 251 GcUtils.runGcAndFinalizersSync(); 252 Log.i(TAG, "Ran gc and finalizers (" + (SystemClock.elapsedRealtime() - t) + "ms)"); 253 } 254 255 /** 256 * Dumps information about the set of SurfaceControls in the registry. 257 * 258 * @param limit the number of layers to report 259 * @param runGc whether to run the GC and finalizers before dumping 260 * @hide 261 */ dump(int limit, boolean runGc, PrintWriter pw)262 public static void dump(int limit, boolean runGc, PrintWriter pw) { 263 if (runGc) { 264 // This needs to run outside the lock since finalization isn't synchronous 265 runGcAndFinalizers(); 266 } 267 synchronized (sLock) { 268 if (sProcessRegistry != null) { 269 sDefaultReporter.onMaxLayersExceeded(sProcessRegistry.mSurfaceControls, limit, pw); 270 } 271 } 272 } 273 } 274