• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
recordBucketCreation(int widthBucket, int heightBucket)45     public void recordBucketCreation(int widthBucket, int heightBucket) {
46         super.recordBucketCreation(widthBucket, heightBucket);
47     }
48 
49     @Override
fitsMaxCacheSize(int width, int height, long maxCacheSize)50     public boolean fitsMaxCacheSize(int width, int height, long maxCacheSize) {
51         return super.fitsMaxCacheSize(width, height, maxCacheSize);
52     }
53 
54     @Override
clear()55     public void clear() {
56         super.clear();
57 
58         mRequestedTotalBytes = 0;
59         mAllocatedOutsidePoolBytes = 0;
60         mTooBigForPoolCount = 0;
61         mCallStack.clear();
62     }
63 
64     @Override
tooBigForCache()65     public void tooBigForCache() {
66         super.tooBigForCache();
67     }
68 
69     /** Used for Debugging only */
70     @Override
recordBucketRequest(int w, int h)71     public void recordBucketRequest(int w, int h) {
72         mRequestedTotalBytes += (w * h * ESTIMATED_PIXEL_BYTES);
73     }
74 
75     @Override
recordAllocOutsidePool(int width, int height)76     public void recordAllocOutsidePool(int width, int height) {
77         mAllocatedOutsidePoolBytes += (width * height * ESTIMATED_PIXEL_BYTES);
78     }
79 
80     @Override
acquiredImage(Integer imageHash)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
disposeImage(Integer imageHash)94     public void disposeImage(Integer imageHash) {
95         mCallStack.remove(imageHash);
96     }
97 
98     @Override
start()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 
calculateGcStatAndReturn()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
getStatistic()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