Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 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 | |
Richard Uhler | 2687050 | 2017-07-04 15:55:19 +0100 | [diff] [blame] | 19 | import com.android.ahat.dominators.DominatorsComputation; |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 20 | import com.android.tools.perflib.captures.DataBuffer; |
| 21 | import com.android.tools.perflib.captures.MemoryMappedFileBuffer; |
| 22 | import com.android.tools.perflib.heap.ArrayInstance; |
| 23 | import com.android.tools.perflib.heap.ClassInstance; |
| 24 | import com.android.tools.perflib.heap.ClassObj; |
| 25 | import com.android.tools.perflib.heap.Heap; |
| 26 | import com.android.tools.perflib.heap.Instance; |
| 27 | import com.android.tools.perflib.heap.ProguardMap; |
| 28 | import com.android.tools.perflib.heap.RootObj; |
| 29 | import com.android.tools.perflib.heap.Snapshot; |
| 30 | import com.android.tools.perflib.heap.StackFrame; |
| 31 | import com.android.tools.perflib.heap.StackTrace; |
| 32 | import gnu.trove.TObjectProcedure; |
| 33 | import java.io.File; |
| 34 | import java.io.IOException; |
| 35 | import java.util.ArrayList; |
| 36 | import java.util.Collection; |
| 37 | import java.util.Comparator; |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 38 | import java.util.List; |
| 39 | import java.util.Map; |
| 40 | |
Richard Uhler | f629cfd | 2016-12-12 13:11:26 +0000 | [diff] [blame] | 41 | public class AhatSnapshot implements Diffable<AhatSnapshot> { |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 42 | private final Site mRootSite = new Site("ROOT"); |
| 43 | |
| 44 | // Collection of objects whose immediate dominator is the SENTINEL_ROOT. |
Richard Uhler | 2687050 | 2017-07-04 15:55:19 +0100 | [diff] [blame] | 45 | private final List<AhatInstance> mRooted; |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 46 | |
| 47 | // List of all ahat instances stored in increasing order by id. |
| 48 | private final List<AhatInstance> mInstances = new ArrayList<AhatInstance>(); |
| 49 | |
Richard Uhler | f629cfd | 2016-12-12 13:11:26 +0000 | [diff] [blame] | 50 | private final List<AhatHeap> mHeaps = new ArrayList<AhatHeap>(); |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 51 | |
Richard Uhler | f629cfd | 2016-12-12 13:11:26 +0000 | [diff] [blame] | 52 | private AhatSnapshot mBaseline = this; |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 53 | |
| 54 | /** |
| 55 | * Create an AhatSnapshot from an hprof file. |
| 56 | */ |
| 57 | public static AhatSnapshot fromHprof(File hprof, ProguardMap map) throws IOException { |
| 58 | return fromDataBuffer(new MemoryMappedFileBuffer(hprof), map); |
| 59 | } |
| 60 | |
| 61 | /** |
| 62 | * Create an AhatSnapshot from an in-memory data buffer. |
| 63 | */ |
| 64 | public static AhatSnapshot fromDataBuffer(DataBuffer buffer, ProguardMap map) throws IOException { |
| 65 | AhatSnapshot snapshot = new AhatSnapshot(buffer, map); |
| 66 | |
| 67 | // Request a GC now to clean up memory used by perflib. This helps to |
| 68 | // avoid a noticable pause when visiting the first interesting page in |
| 69 | // ahat. |
| 70 | System.gc(); |
| 71 | |
| 72 | return snapshot; |
| 73 | } |
| 74 | |
| 75 | /** |
| 76 | * Constructs an AhatSnapshot for the given hprof binary data. |
| 77 | */ |
| 78 | private AhatSnapshot(DataBuffer buffer, ProguardMap map) throws IOException { |
| 79 | Snapshot snapshot = Snapshot.createSnapshot(buffer, map); |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 80 | |
Richard Uhler | 3ee4bff | 2017-05-16 13:31:01 +0100 | [diff] [blame] | 81 | // Properly label the class of class objects in the perflib snapshot. |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 82 | final ClassObj javaLangClass = snapshot.findClass("java.lang.Class"); |
| 83 | if (javaLangClass != null) { |
| 84 | for (Heap heap : snapshot.getHeaps()) { |
| 85 | Collection<ClassObj> classes = heap.getClasses(); |
| 86 | for (ClassObj clsObj : classes) { |
| 87 | if (clsObj.getClassObj() == null) { |
| 88 | clsObj.setClassId(javaLangClass.getId()); |
| 89 | } |
| 90 | } |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | // Create mappings from id to ahat instance and heaps. |
| 95 | Collection<Heap> heaps = snapshot.getHeaps(); |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 96 | for (Heap heap : heaps) { |
Richard Uhler | f629cfd | 2016-12-12 13:11:26 +0000 | [diff] [blame] | 97 | // Note: mHeaps will not be in index order if snapshot.getHeaps does not |
| 98 | // return heaps in index order. That's fine, because we don't rely on |
| 99 | // mHeaps being in index order. |
| 100 | mHeaps.add(new AhatHeap(heap.getName(), snapshot.getHeapIndex(heap))); |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 101 | TObjectProcedure<Instance> doCreate = new TObjectProcedure<Instance>() { |
| 102 | @Override |
| 103 | public boolean execute(Instance inst) { |
Richard Uhler | d640e29 | 2016-12-28 15:46:03 +0000 | [diff] [blame] | 104 | long id = inst.getId(); |
| 105 | if (inst instanceof ClassInstance) { |
| 106 | mInstances.add(new AhatClassInstance(id)); |
| 107 | } else if (inst instanceof ArrayInstance) { |
| 108 | mInstances.add(new AhatArrayInstance(id)); |
| 109 | } else if (inst instanceof ClassObj) { |
| 110 | AhatClassObj classObj = new AhatClassObj(id); |
| 111 | mInstances.add(classObj); |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 112 | } |
| 113 | return true; |
| 114 | } |
| 115 | }; |
| 116 | for (Instance instance : heap.getClasses()) { |
| 117 | doCreate.execute(instance); |
| 118 | } |
| 119 | heap.forEachInstance(doCreate); |
| 120 | } |
| 121 | |
| 122 | // Sort the instances by id so we can use binary search to lookup |
| 123 | // instances by id. |
| 124 | mInstances.sort(new Comparator<AhatInstance>() { |
| 125 | @Override |
| 126 | public int compare(AhatInstance a, AhatInstance b) { |
| 127 | return Long.compare(a.getId(), b.getId()); |
| 128 | } |
| 129 | }); |
| 130 | |
Richard Uhler | 3ee4bff | 2017-05-16 13:31:01 +0100 | [diff] [blame] | 131 | Map<Instance, Long> registeredNative = Perflib.getRegisteredNativeAllocations(snapshot); |
| 132 | |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 133 | // Initialize ahat snapshot and instances based on the perflib snapshot |
| 134 | // and instances. |
| 135 | for (AhatInstance ahat : mInstances) { |
| 136 | Instance inst = snapshot.findInstance(ahat.getId()); |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 137 | |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 138 | StackFrame[] frames = null; |
| 139 | StackTrace stack = inst.getStack(); |
| 140 | if (stack != null) { |
| 141 | frames = stack.getFrames(); |
| 142 | } |
Richard Uhler | 29c4bee | 2017-08-10 10:16:15 +0100 | [diff] [blame] | 143 | ahat.initialize(this, inst, mRootSite.getSite(frames)); |
Richard Uhler | 2687050 | 2017-07-04 15:55:19 +0100 | [diff] [blame] | 144 | |
| 145 | Long registeredNativeSize = registeredNative.get(inst); |
| 146 | if (registeredNativeSize != null) { |
| 147 | ahat.addRegisteredNativeSize(registeredNativeSize); |
| 148 | } |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 149 | } |
| 150 | |
| 151 | // Record the roots and their types. |
Richard Uhler | 2687050 | 2017-07-04 15:55:19 +0100 | [diff] [blame] | 152 | SuperRoot superRoot = new SuperRoot(); |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 153 | for (RootObj root : snapshot.getGCRoots()) { |
| 154 | Instance inst = root.getReferredInstance(); |
| 155 | if (inst != null) { |
Richard Uhler | 2687050 | 2017-07-04 15:55:19 +0100 | [diff] [blame] | 156 | AhatInstance ahat = findInstance(inst.getId()); |
| 157 | if (!ahat.isRoot()) { |
| 158 | superRoot.addRoot(ahat); |
| 159 | } |
| 160 | ahat.addRootType(root.getRootType().toString()); |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 161 | } |
| 162 | } |
| 163 | snapshot.dispose(); |
Richard Uhler | 3ee4bff | 2017-05-16 13:31:01 +0100 | [diff] [blame] | 164 | |
Richard Uhler | 2687050 | 2017-07-04 15:55:19 +0100 | [diff] [blame] | 165 | AhatInstance.computeReverseReferences(superRoot); |
| 166 | DominatorsComputation.computeDominators(superRoot); |
| 167 | AhatInstance.computeRetainedSize(superRoot, mHeaps.size()); |
| 168 | |
| 169 | mRooted = superRoot.getDominated(); |
| 170 | for (AhatHeap heap : mHeaps) { |
| 171 | heap.addToSize(superRoot.getRetainedSize(heap)); |
Richard Uhler | 3ee4bff | 2017-05-16 13:31:01 +0100 | [diff] [blame] | 172 | } |
Richard Uhler | 2687050 | 2017-07-04 15:55:19 +0100 | [diff] [blame] | 173 | |
Richard Uhler | 29c4bee | 2017-08-10 10:16:15 +0100 | [diff] [blame] | 174 | mRootSite.prepareForUse(0, mHeaps.size()); |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 175 | } |
| 176 | |
| 177 | /** |
| 178 | * Returns the instance with given id in this snapshot. |
| 179 | * Returns null if no instance with the given id is found. |
| 180 | */ |
| 181 | public AhatInstance findInstance(long id) { |
| 182 | // Binary search over the sorted instances. |
| 183 | int start = 0; |
| 184 | int end = mInstances.size(); |
| 185 | while (start < end) { |
| 186 | int mid = start + ((end - start) / 2); |
| 187 | AhatInstance midInst = mInstances.get(mid); |
| 188 | long midId = midInst.getId(); |
| 189 | if (id == midId) { |
| 190 | return midInst; |
| 191 | } else if (id < midId) { |
| 192 | end = mid; |
| 193 | } else { |
| 194 | start = mid + 1; |
| 195 | } |
| 196 | } |
| 197 | return null; |
| 198 | } |
| 199 | |
| 200 | /** |
| 201 | * Returns the AhatClassObj with given id in this snapshot. |
| 202 | * Returns null if no class object with the given id is found. |
| 203 | */ |
| 204 | public AhatClassObj findClassObj(long id) { |
| 205 | AhatInstance inst = findInstance(id); |
| 206 | return inst == null ? null : inst.asClassObj(); |
| 207 | } |
| 208 | |
| 209 | /** |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 210 | * Returns the heap with the given name, if any. |
| 211 | * Returns null if no heap with the given name could be found. |
| 212 | */ |
| 213 | public AhatHeap getHeap(String name) { |
| 214 | // We expect a small number of heaps (maybe 3 or 4 total), so a linear |
| 215 | // search should be acceptable here performance wise. |
| 216 | for (AhatHeap heap : getHeaps()) { |
| 217 | if (heap.getName().equals(name)) { |
| 218 | return heap; |
| 219 | } |
| 220 | } |
| 221 | return null; |
| 222 | } |
| 223 | |
| 224 | /** |
| 225 | * Returns a list of heaps in the snapshot in canonical order. |
Richard Uhler | f629cfd | 2016-12-12 13:11:26 +0000 | [diff] [blame] | 226 | * Modifications to the returned list are visible to this AhatSnapshot, |
| 227 | * which is used by diff to insert place holder heaps. |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 228 | */ |
Richard Uhler | f629cfd | 2016-12-12 13:11:26 +0000 | [diff] [blame] | 229 | public List<AhatHeap> getHeaps() { |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 230 | return mHeaps; |
| 231 | } |
| 232 | |
| 233 | /** |
| 234 | * Returns a collection of instances whose immediate dominator is the |
| 235 | * SENTINEL_ROOT. |
| 236 | */ |
| 237 | public List<AhatInstance> getRooted() { |
| 238 | return mRooted; |
| 239 | } |
| 240 | |
| 241 | /** |
Richard Uhler | f629cfd | 2016-12-12 13:11:26 +0000 | [diff] [blame] | 242 | * Returns the root site for this snapshot. |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 243 | */ |
Richard Uhler | f629cfd | 2016-12-12 13:11:26 +0000 | [diff] [blame] | 244 | public Site getRootSite() { |
| 245 | return mRootSite; |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 246 | } |
| 247 | |
Richard Uhler | 29c4bee | 2017-08-10 10:16:15 +0100 | [diff] [blame] | 248 | // Get the site associated with the given id. |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 249 | // Returns the root site if no such site found. |
Richard Uhler | 29c4bee | 2017-08-10 10:16:15 +0100 | [diff] [blame] | 250 | public Site getSite(long id) { |
| 251 | Site site = mRootSite.findSite(id); |
| 252 | return site == null ? mRootSite : site; |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 253 | } |
| 254 | |
| 255 | // Return the Value for the given perflib value object. |
| 256 | Value getValue(Object value) { |
| 257 | if (value instanceof Instance) { |
| 258 | value = findInstance(((Instance)value).getId()); |
| 259 | } |
Richard Uhler | 21f89d2 | 2017-08-26 16:30:04 +0100 | [diff] [blame] | 260 | return Value.pack(value); |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 261 | } |
Richard Uhler | f629cfd | 2016-12-12 13:11:26 +0000 | [diff] [blame] | 262 | |
| 263 | public void setBaseline(AhatSnapshot baseline) { |
| 264 | mBaseline = baseline; |
| 265 | } |
| 266 | |
| 267 | /** |
| 268 | * Returns true if this snapshot has been diffed against another, different |
| 269 | * snapshot. |
| 270 | */ |
| 271 | public boolean isDiffed() { |
| 272 | return mBaseline != this; |
| 273 | } |
| 274 | |
| 275 | @Override public AhatSnapshot getBaseline() { |
| 276 | return mBaseline; |
| 277 | } |
| 278 | |
| 279 | @Override public boolean isPlaceHolder() { |
| 280 | return false; |
| 281 | } |
Richard Uhler | cda4f2e | 2016-09-09 09:56:20 +0100 | [diff] [blame] | 282 | } |