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