• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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