1 /* 2 * Copyright (C) 2007 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.ddmlib; 18 19 import java.util.ArrayList; 20 import java.util.Iterator; 21 import java.util.List; 22 23 /** 24 * Stores native allocation information. 25 * <p/>Contains number of allocations, their size and the stack trace. 26 * <p/>Note: the ddmlib does not resolve the stack trace automatically. While this class provides 27 * storage for resolved stack trace, this is merely for convenience. 28 */ 29 public final class NativeAllocationInfo { 30 /* constants for flag bits */ 31 private static final int FLAG_ZYGOTE_CHILD = (1<<31); 32 private static final int FLAG_MASK = (FLAG_ZYGOTE_CHILD); 33 34 /** 35 * list of alloc functions that are filtered out when attempting to display 36 * a relevant method responsible for an allocation 37 */ 38 private static ArrayList<String> sAllocFunctionFilter; 39 static { 40 sAllocFunctionFilter = new ArrayList<String>(); 41 sAllocFunctionFilter.add("malloc"); //$NON-NLS-1$ 42 sAllocFunctionFilter.add("calloc"); //$NON-NLS-1$ 43 sAllocFunctionFilter.add("realloc"); //$NON-NLS-1$ 44 sAllocFunctionFilter.add("get_backtrace"); //$NON-NLS-1$ 45 sAllocFunctionFilter.add("get_hash"); //$NON-NLS-1$ 46 sAllocFunctionFilter.add("??"); //$NON-NLS-1$ 47 sAllocFunctionFilter.add("internal_free"); //$NON-NLS-1$ 48 sAllocFunctionFilter.add("operator new"); //$NON-NLS-1$ 49 sAllocFunctionFilter.add("leak_free"); //$NON-NLS-1$ 50 sAllocFunctionFilter.add("chk_free"); //$NON-NLS-1$ 51 sAllocFunctionFilter.add("chk_memalign"); //$NON-NLS-1$ 52 sAllocFunctionFilter.add("Malloc"); //$NON-NLS-1$ 53 sAllocFunctionFilter.add("leak_memalign"); //$NON-NLS-1$ 54 } 55 56 private final int mSize; 57 58 private final boolean mIsZygoteChild; 59 60 private final int mAllocations; 61 62 private final ArrayList<Long> mStackCallAddresses = new ArrayList<Long>(); 63 64 private ArrayList<NativeStackCallInfo> mResolvedStackCall = null; 65 66 private boolean mIsStackCallResolved = false; 67 68 /** 69 * Constructs a new {@link NativeAllocationInfo}. 70 * @param size The size of the allocations. 71 * @param allocations the allocation count 72 */ NativeAllocationInfo(int size, int allocations)73 NativeAllocationInfo(int size, int allocations) { 74 this.mSize = size & ~FLAG_MASK; 75 this.mIsZygoteChild = ((size & FLAG_ZYGOTE_CHILD) != 0); 76 this.mAllocations = allocations; 77 } 78 79 /** 80 * Adds a stack call address for this allocation. 81 * @param address The address to add. 82 */ addStackCallAddress(long address)83 void addStackCallAddress(long address) { 84 mStackCallAddresses.add(address); 85 } 86 87 /** 88 * Returns the total size of this allocation. 89 */ getSize()90 public int getSize() { 91 return mSize; 92 } 93 94 /** 95 * Returns whether the allocation happened in a child of the zygote 96 * process. 97 */ isZygoteChild()98 public boolean isZygoteChild() { 99 return mIsZygoteChild; 100 } 101 102 /** 103 * Returns the allocation count. 104 */ getAllocationCount()105 public int getAllocationCount() { 106 return mAllocations; 107 } 108 109 /** 110 * Returns whether the stack call addresses have been resolved into 111 * {@link NativeStackCallInfo} objects. 112 */ isStackCallResolved()113 public boolean isStackCallResolved() { 114 return mIsStackCallResolved; 115 } 116 117 /** 118 * Returns the stack call of this allocation as raw addresses. 119 * @return the list of addresses where the allocation happened. 120 */ getStackCallAddresses()121 public Long[] getStackCallAddresses() { 122 return mStackCallAddresses.toArray(new Long[mStackCallAddresses.size()]); 123 } 124 125 /** 126 * Sets the resolved stack call for this allocation. 127 * <p/> 128 * If <code>resolvedStackCall</code> is non <code>null</code> then 129 * {@link #isStackCallResolved()} will return <code>true</code> after this call. 130 * @param resolvedStackCall The list of {@link NativeStackCallInfo}. 131 */ setResolvedStackCall(List<NativeStackCallInfo> resolvedStackCall)132 public synchronized void setResolvedStackCall(List<NativeStackCallInfo> resolvedStackCall) { 133 if (mResolvedStackCall == null) { 134 mResolvedStackCall = new ArrayList<NativeStackCallInfo>(); 135 } else { 136 mResolvedStackCall.clear(); 137 } 138 mResolvedStackCall.addAll(resolvedStackCall); 139 mIsStackCallResolved = mResolvedStackCall.size() != 0; 140 } 141 142 /** 143 * Returns the resolved stack call. 144 * @return An array of {@link NativeStackCallInfo} or <code>null</code> if the stack call 145 * was not resolved. 146 * @see #setResolvedStackCall(ArrayList) 147 * @see #isStackCallResolved() 148 */ getResolvedStackCall()149 public synchronized NativeStackCallInfo[] getResolvedStackCall() { 150 if (mIsStackCallResolved) { 151 return mResolvedStackCall.toArray(new NativeStackCallInfo[mResolvedStackCall.size()]); 152 } 153 154 return null; 155 } 156 157 /** 158 * Indicates whether some other object is "equal to" this one. 159 * @param obj the reference object with which to compare. 160 * @return <code>true</code> if this object is equal to the obj argument; 161 * <code>false</code> otherwise. 162 * @see java.lang.Object#equals(java.lang.Object) 163 */ 164 @Override equals(Object obj)165 public boolean equals(Object obj) { 166 if (obj == this) 167 return true; 168 if (obj instanceof NativeAllocationInfo) { 169 NativeAllocationInfo mi = (NativeAllocationInfo)obj; 170 // quick compare of size, alloc, and stackcall size 171 if (mSize != mi.mSize || mAllocations != mi.mAllocations || 172 mStackCallAddresses.size() != mi.mStackCallAddresses.size()) { 173 return false; 174 } 175 // compare the stack addresses 176 int count = mStackCallAddresses.size(); 177 for (int i = 0 ; i < count ; i++) { 178 long a = mStackCallAddresses.get(i); 179 long b = mi.mStackCallAddresses.get(i); 180 if (a != b) { 181 return false; 182 } 183 } 184 185 return true; 186 } 187 return false; 188 } 189 190 /** 191 * Returns a string representation of the object. 192 * @see java.lang.Object#toString() 193 */ 194 @Override toString()195 public String toString() { 196 StringBuffer buffer = new StringBuffer(); 197 buffer.append("Allocations: "); 198 buffer.append(mAllocations); 199 buffer.append("\n"); //$NON-NLS-1$ 200 201 buffer.append("Size: "); 202 buffer.append(mSize); 203 buffer.append("\n"); //$NON-NLS-1$ 204 205 buffer.append("Total Size: "); 206 buffer.append(mSize * mAllocations); 207 buffer.append("\n"); //$NON-NLS-1$ 208 209 Iterator<Long> addrIterator = mStackCallAddresses.iterator(); 210 Iterator<NativeStackCallInfo> sourceIterator = mResolvedStackCall.iterator(); 211 212 while (sourceIterator.hasNext()) { 213 long addr = addrIterator.next(); 214 NativeStackCallInfo source = sourceIterator.next(); 215 if (addr == 0) 216 continue; 217 218 if (source.getLineNumber() != -1) { 219 buffer.append(String.format("\t%1$08x\t%2$s --- %3$s --- %4$s:%5$d\n", addr, 220 source.getLibraryName(), source.getMethodName(), 221 source.getSourceFile(), source.getLineNumber())); 222 } else { 223 buffer.append(String.format("\t%1$08x\t%2$s --- %3$s --- %4$s\n", addr, 224 source.getLibraryName(), source.getMethodName(), source.getSourceFile())); 225 } 226 } 227 228 return buffer.toString(); 229 } 230 231 /** 232 * Returns the first {@link NativeStackCallInfo} that is relevant. 233 * <p/> 234 * A relevant <code>NativeStackCallInfo</code> is a stack call that is not deep in the 235 * lower level of the libc, but the actual method that performed the allocation. 236 * @return a <code>NativeStackCallInfo</code> or <code>null</code> if the stack call has not 237 * been processed from the raw addresses. 238 * @see #setResolvedStackCall(ArrayList) 239 * @see #isStackCallResolved() 240 */ getRelevantStackCallInfo()241 public synchronized NativeStackCallInfo getRelevantStackCallInfo() { 242 if (mIsStackCallResolved && mResolvedStackCall != null) { 243 Iterator<NativeStackCallInfo> sourceIterator = mResolvedStackCall.iterator(); 244 Iterator<Long> addrIterator = mStackCallAddresses.iterator(); 245 246 while (sourceIterator.hasNext() && addrIterator.hasNext()) { 247 long addr = addrIterator.next(); 248 NativeStackCallInfo info = sourceIterator.next(); 249 if (addr != 0 && info != null) { 250 if (isRelevant(info.getMethodName())) { 251 return info; 252 } 253 } 254 } 255 256 // couldnt find a relevant one, so we'll return the first one if it 257 // exists. 258 if (mResolvedStackCall.size() > 0) 259 return mResolvedStackCall.get(0); 260 } 261 262 return null; 263 } 264 265 /** 266 * Returns true if the method name is relevant. 267 * @param methodName the method name to test. 268 */ isRelevant(String methodName)269 private boolean isRelevant(String methodName) { 270 for (String filter : sAllocFunctionFilter) { 271 if (methodName.contains(filter)) { 272 return false; 273 } 274 } 275 276 return true; 277 } 278 } 279