Home | History | Annotate | Download | only in imagepool
      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 android.util.imagepool;
     18 
     19 import java.lang.management.GarbageCollectorMXBean;
     20 import java.lang.management.ManagementFactory;
     21 import java.util.HashMap;
     22 import java.util.Map;
     23 
     24 import com.google.common.collect.HashMultiset;
     25 import com.google.common.collect.Multiset;
     26 
     27 /**
     28  * Useful impl for debugging reproable error.
     29  */
     30 public class ImagePoolStatsDebugImpl extends ImagePoolStatsProdImpl {
     31 
     32     private static String PACKAGE_NAME = ImagePoolStats.class.getPackage().getName();
     33 
     34     // Used for deugging purposes only.
     35     private final Map<Integer, String> mCallStack = new HashMap<>();
     36     private long mRequestedTotalBytes = 0;
     37     private long mAllocatedOutsidePoolBytes = 0;
     38 
     39     // Used for gc-related stats.
     40     private long mPreviousGcCollection = 0;
     41     private long mPreviousGcTime = 0;
     42 
     43     /** Used for policy */
     44     @Override
     45     public void recordBucketCreation(int widthBucket, int heightBucket) {
     46         super.recordBucketCreation(widthBucket, heightBucket);
     47     }
     48 
     49     @Override
     50     public boolean fitsMaxCacheSize(int width, int height, long maxCacheSize) {
     51         return super.fitsMaxCacheSize(width, height, maxCacheSize);
     52     }
     53 
     54     @Override
     55     public void clear() {
     56         super.clear();
     57 
     58         mRequestedTotalBytes = 0;
     59         mAllocatedOutsidePoolBytes = 0;
     60         mTooBigForPoolCount = 0;
     61         mCallStack.clear();
     62     }
     63 
     64     @Override
     65     public void tooBigForCache() {
     66         super.tooBigForCache();
     67     }
     68 
     69     /** Used for Debugging only */
     70     @Override
     71     public void recordBucketRequest(int w, int h) {
     72         mRequestedTotalBytes += (w * h * ESTIMATED_PIXEL_BYTES);
     73     }
     74 
     75     @Override
     76     public void recordAllocOutsidePool(int width, int height) {
     77         mAllocatedOutsidePoolBytes += (width * height * ESTIMATED_PIXEL_BYTES);
     78     }
     79 
     80     @Override
     81     public void acquiredImage(Integer imageHash) {
     82         for (int i = 1; i < Thread.currentThread().getStackTrace().length; i++) {
     83             StackTraceElement element = Thread.currentThread().getStackTrace()[i];
     84             String str = element.toString();
     85 
     86             if (!str.contains(PACKAGE_NAME)) {
     87                 mCallStack.put(imageHash, str);
     88                 break;
     89             }
     90         }
     91     }
     92 
     93     @Override
     94     public void disposeImage(Integer imageHash) {
     95         mCallStack.remove(imageHash);
     96     }
     97 
     98     @Override
     99     public void start() {
    100         long totalGarbageCollections = 0;
    101         long garbageCollectionTime = 0;
    102         for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
    103             long count = gc.getCollectionCount();
    104             if (count >= 0) {
    105                 totalGarbageCollections += count;
    106             }
    107             long time = gc.getCollectionTime();
    108             if (time >= 0) {
    109                 garbageCollectionTime += time;
    110             }
    111         }
    112         mPreviousGcCollection = totalGarbageCollections;
    113         mPreviousGcTime = garbageCollectionTime;
    114     }
    115 
    116     private String calculateGcStatAndReturn() {
    117         long totalGarbageCollections = 0;
    118         long garbageCollectionTime = 0;
    119         for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
    120             long count = gc.getCollectionCount();
    121             if (count > 0) {
    122                 totalGarbageCollections += count;
    123             }
    124             long time = gc.getCollectionTime();
    125             if(time > 0) {
    126                 garbageCollectionTime += time;
    127             }
    128         }
    129         totalGarbageCollections -= mPreviousGcCollection;
    130         garbageCollectionTime -= mPreviousGcTime;
    131 
    132         StringBuilder builder = new StringBuilder();
    133         builder.append("Total Garbage Collections: ");
    134         builder.append(totalGarbageCollections);
    135         builder.append("\n");
    136 
    137         builder.append("Total Garbage Collection Time (ms): ");
    138         builder.append(garbageCollectionTime);
    139         builder.append("\n");
    140 
    141         return builder.toString();
    142     }
    143 
    144     @Override
    145     public String getStatistic() {
    146         StringBuilder builder = new StringBuilder();
    147 
    148         builder.append(calculateGcStatAndReturn());
    149         builder.append("Memory\n");
    150         builder.append(" requested total         : ");
    151         builder.append(mRequestedTotalBytes / 1_000_000);
    152         builder.append(" MB\n");
    153         builder.append(" allocated (in pool)     : ");
    154         builder.append(mAllocateTotalBytes / 1_000_000);
    155         builder.append(" MB\n");
    156         builder.append(" allocated (out of pool) : ");
    157         builder.append(mAllocatedOutsidePoolBytes / 1_000_000);
    158         builder.append(" MB\n");
    159 
    160         double percent = (1.0 - (double) mRequestedTotalBytes / (mAllocateTotalBytes +
    161                 mAllocatedOutsidePoolBytes));
    162         if (percent < 0.0) {
    163             builder.append(" saved : ");
    164             builder.append(-1.0 * percent);
    165             builder.append("%\n");
    166         } else {
    167             builder.append(" wasting : ");
    168             builder.append(percent);
    169             builder.append("%\n");
    170         }
    171 
    172         builder.append("Undispose images\n");
    173         Multiset<String> countSet = HashMultiset.create();
    174         for (String callsite : mCallStack.values()) {
    175             countSet.add(callsite);
    176         }
    177 
    178         for (Multiset.Entry<String> entry : countSet.entrySet()) {
    179             builder.append(" - ");
    180             builder.append(entry.getElement());
    181             builder.append(" - missed dispose : ");
    182             builder.append(entry.getCount());
    183             builder.append(" times\n");
    184         }
    185 
    186         builder.append("Number of times requested image didn't fit the pool : ");
    187         builder.append(mTooBigForPoolCount);
    188         builder.append("\n");
    189 
    190         return builder.toString();
    191     }
    192 }
    193