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 com.android.ahat.heapdump;
18 
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Comparator;
22 import java.util.Iterator;
23 import java.util.List;
24 
25 /**
26  * Provides Comparators and helper functions for sorting Instances, Sites, and
27  * other things.
28  * <p>
29  * Note: The Comparators defined here impose orderings that are inconsistent
30  * with equals. They should not be used for element lookup or search. They
31  * should only be used for showing elements to the user in different orders.
32  */
33 public class Sort {
34   /**
35    * Compares sizes by their total size.
36    * This sorts sizes from smaller total size to larger total size.
37    */
38   public static final Comparator<Size> SIZE_BY_SIZE = new Comparator<Size>() {
39     @Override
40     public int compare(Size a, Size b) {
41       return Long.compare(a.getSize(), b.getSize());
42     }
43   };
44 
45   /**
46    * Compares instances by their total retained size.
47    * Different instances with the same total retained size are considered
48    * equal for the purposes of comparison.
49    * This sorts instances from larger retained size to smaller retained size.
50    */
51   public static final Comparator<AhatInstance> INSTANCE_BY_TOTAL_RETAINED_SIZE
52     = new Comparator<AhatInstance>() {
53     @Override
54     public int compare(AhatInstance a, AhatInstance b) {
55       return SIZE_BY_SIZE.compare(b.getTotalRetainedSize(), a.getTotalRetainedSize());
56     }
57   };
58 
59   /**
60    * Compares instances by their retained size for a given heap index.
61    * Different instances with the same total retained size are considered
62    * equal for the purposes of comparison.
63    * This sorts instances from larger retained size to smaller retained size.
64    */
65   private static class InstanceByHeapRetainedSize implements Comparator<AhatInstance> {
66     private AhatHeap mHeap;
67 
InstanceByHeapRetainedSize(AhatHeap heap)68     public InstanceByHeapRetainedSize(AhatHeap heap) {
69       mHeap = heap;
70     }
71 
72     @Override
compare(AhatInstance a, AhatInstance b)73     public int compare(AhatInstance a, AhatInstance b) {
74       return SIZE_BY_SIZE.compare(b.getRetainedSize(mHeap), a.getRetainedSize(mHeap));
75     }
76   }
77 
78   /**
79    * Compares objects based on a list of comparators, giving priority to the
80    * earlier comparators in the list.
81    */
82   private static class WithPriority<T> implements Comparator<T> {
83     private List<Comparator<T>> mComparators;
84 
85     /**
86      * Constructs a comparator giving sort priority to earlier comparators in
87      * the list.
88      *
89      * @param comparators the list of comparators to use for sorting
90      */
WithPriority(Comparator<T>.... comparators)91     public WithPriority(Comparator<T>... comparators) {
92       mComparators = Arrays.asList(comparators);
93     }
94 
95     /**
96      * Constructs a comparator giving sort priority to earlier comparators in
97      * the list.
98      *
99      * @param comparators the list of comparators to use for sorting
100      */
WithPriority(List<Comparator<T>> comparators)101     public WithPriority(List<Comparator<T>> comparators) {
102       mComparators = comparators;
103     }
104 
105     @Override
compare(T a, T b)106     public int compare(T a, T b) {
107       int res = 0;
108       Iterator<Comparator<T>> iter = mComparators.iterator();
109       while (res == 0 && iter.hasNext()) {
110         res = iter.next().compare(a, b);
111       }
112       return res;
113     }
114   }
115 
116   /**
117    * Returns a comparator that gives sort priority to earlier comparators in
118    * the list.
119    *
120    * @param <T> the type of object being sorted
121    * @param comparators the list of comparators to use for sorting
122    * @return the composite comparator
123    */
withPriority(Comparator<T>.... comparators)124   public static <T> Comparator<T> withPriority(Comparator<T>... comparators) {
125     return new WithPriority(comparators);
126   }
127 
128   /**
129    * Returns a comparator that gives a default instance sort for the given
130    * snapshot.
131    * Objects are sorted by retained size, with priority given to the "app"
132    * heap if present.
133    *
134    * @param snapshot the snapshot to use the comparator with
135    * @return the default instance comparator
136    */
defaultInstanceCompare(AhatSnapshot snapshot)137   public static Comparator<AhatInstance> defaultInstanceCompare(AhatSnapshot snapshot) {
138     List<Comparator<AhatInstance>> comparators = new ArrayList<Comparator<AhatInstance>>();
139 
140     // Priority goes to the app heap, if we can find one.
141     AhatHeap appHeap = snapshot.getHeap("app");
142     if (appHeap != null) {
143       comparators.add(new InstanceByHeapRetainedSize(appHeap));
144     }
145 
146     // Next is by total retained size.
147     comparators.add(INSTANCE_BY_TOTAL_RETAINED_SIZE);
148     return new WithPriority<AhatInstance>(comparators);
149   }
150 
151   /**
152    * Compares Sites by the size of objects allocated on a given heap.
153    * Different object infos with the same size on the given heap are
154    * considered equal for the purposes of comparison.
155    * This sorts sites from larger size to smaller size.
156    */
157   private static class SiteByHeapSize implements Comparator<Site> {
158     AhatHeap mHeap;
159 
160     /**
161      * Constructs a SiteByHeapSize comparator.
162      *
163      * @param heap the heap to use when comparing sizes
164      */
SiteByHeapSize(AhatHeap heap)165     public SiteByHeapSize(AhatHeap heap) {
166       mHeap = heap;
167     }
168 
169     @Override
compare(Site a, Site b)170     public int compare(Site a, Site b) {
171       return SIZE_BY_SIZE.compare(b.getSize(mHeap), a.getSize(mHeap));
172     }
173   }
174 
175   /**
176    * Compares Sites by the total size of objects allocated.
177    * This sorts sites from larger size to smaller size.
178    */
179   public static final Comparator<Site> SITE_BY_TOTAL_SIZE = new Comparator<Site>() {
180     @Override
181     public int compare(Site a, Site b) {
182       return SIZE_BY_SIZE.compare(b.getTotalSize(), a.getTotalSize());
183     }
184   };
185 
186   /**
187    * Compares Sites using a default comparison order.
188    * This sorts sites from larger size to smaller size, giving preference to
189    * sites with more allocation on the "app" heap, if present.
190    *
191    * @param snapshot the snapshot to use the comparator with
192    * @return the default site comparator
193    */
defaultSiteCompare(AhatSnapshot snapshot)194   public static Comparator<Site> defaultSiteCompare(AhatSnapshot snapshot) {
195     List<Comparator<Site>> comparators = new ArrayList<Comparator<Site>>();
196 
197     // Priority goes to the app heap, if we can find one.
198     AhatHeap appHeap = snapshot.getHeap("app");
199     if (appHeap != null) {
200       comparators.add(new SiteByHeapSize(appHeap));
201     }
202 
203     // Next is by total size.
204     comparators.add(SITE_BY_TOTAL_SIZE);
205     return new WithPriority<Site>(comparators);
206   }
207 
208   /**
209    * Compare Site.ObjectsInfo by their size.
210    * Different object infos with the same total retained size are considered
211    * equal for the purposes of comparison.
212    * This sorts object infos from larger retained size to smaller size.
213    */
214   public static final Comparator<Site.ObjectsInfo> OBJECTS_INFO_BY_SIZE
215     = new Comparator<Site.ObjectsInfo>() {
216     @Override
217     public int compare(Site.ObjectsInfo a, Site.ObjectsInfo b) {
218       return SIZE_BY_SIZE.compare(b.numBytes, a.numBytes);
219     }
220   };
221 
222   /**
223    * Compares Site.ObjectsInfo by heap name.
224    * Different object infos with the same heap name are considered equal for
225    * the purposes of comparison.
226    */
227   public static final Comparator<Site.ObjectsInfo> OBJECTS_INFO_BY_HEAP_NAME
228     = new Comparator<Site.ObjectsInfo>() {
229     @Override
230     public int compare(Site.ObjectsInfo a, Site.ObjectsInfo b) {
231       return a.heap.getName().compareTo(b.heap.getName());
232     }
233   };
234 
235   /**
236    * Compares Site.ObjectsInfo by class name.
237    * Different object infos with the same class name are considered equal for
238    * the purposes of comparison.
239    */
240   public static final Comparator<Site.ObjectsInfo> OBJECTS_INFO_BY_CLASS_NAME
241     = new Comparator<Site.ObjectsInfo>() {
242     @Override
243     public int compare(Site.ObjectsInfo a, Site.ObjectsInfo b) {
244       String aName = a.getClassName();
245       String bName = b.getClassName();
246       return aName.compareTo(bName);
247     }
248   };
249 
250   /**
251    * Compares FieldValue by field name.
252    */
253   public static final Comparator<FieldValue> FIELD_VALUE_BY_NAME
254     = new Comparator<FieldValue>() {
255     @Override
256     public int compare(FieldValue a, FieldValue b) {
257       return a.name.compareTo(b.name);
258     }
259   };
260 
261   /**
262    * Compares FieldValue by type name.
263    */
264   public static final Comparator<FieldValue> FIELD_VALUE_BY_TYPE
265     = new Comparator<FieldValue>() {
266     @Override
267     public int compare(FieldValue a, FieldValue b) {
268       return a.type.compareTo(b.type);
269     }
270   };
271 }
272 
273