• 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.Arrays;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
25 
26 /**
27  * Stores native allocation information.
28  * <p/>Contains number of allocations, their size and the stack trace.
29  * <p/>Note: the ddmlib does not resolve the stack trace automatically. While this class provides
30  * storage for resolved stack trace, this is merely for convenience.
31  */
32 public final class NativeAllocationInfo {
33     /* Keywords used as delimiters in the string representation of a NativeAllocationInfo */
34     public static final String END_STACKTRACE_KW = "EndStacktrace";
35     public static final String BEGIN_STACKTRACE_KW = "BeginStacktrace:";
36     public static final String TOTAL_SIZE_KW = "TotalSize:";
37     public static final String SIZE_KW = "Size:";
38     public static final String ALLOCATIONS_KW = "Allocations:";
39 
40     /* constants for flag bits */
41     private static final int FLAG_ZYGOTE_CHILD  = (1<<31);
42     private static final int FLAG_MASK          = (FLAG_ZYGOTE_CHILD);
43 
44     /** Libraries whose methods will be assumed to be not part of the user code. */
45     private static final List<String> FILTERED_LIBRARIES = Arrays.asList(new String[] {
46             "libc.so",
47             "libc_malloc_debug_leak.so",
48     });
49 
50     /** Method names that should be assumed to be not part of the user code. */
51     private static final List<Pattern> FILTERED_METHOD_NAME_PATTERNS = Arrays.asList(new Pattern[] {
52             Pattern.compile("malloc", Pattern.CASE_INSENSITIVE),
53             Pattern.compile("calloc", Pattern.CASE_INSENSITIVE),
54             Pattern.compile("realloc", Pattern.CASE_INSENSITIVE),
55             Pattern.compile("operator new", Pattern.CASE_INSENSITIVE),
56             Pattern.compile("memalign", Pattern.CASE_INSENSITIVE),
57     });
58 
59     private final int mSize;
60 
61     private final boolean mIsZygoteChild;
62 
63     private final int mAllocations;
64 
65     private final ArrayList<Long> mStackCallAddresses = new ArrayList<Long>();
66 
67     private ArrayList<NativeStackCallInfo> mResolvedStackCall = null;
68 
69     private boolean mIsStackCallResolved = false;
70 
71     /**
72      * Constructs a new {@link NativeAllocationInfo}.
73      * @param size The size of the allocations.
74      * @param allocations the allocation count
75      */
NativeAllocationInfo(int size, int allocations)76     public NativeAllocationInfo(int size, int allocations) {
77         this.mSize = size & ~FLAG_MASK;
78         this.mIsZygoteChild = ((size & FLAG_ZYGOTE_CHILD) != 0);
79         this.mAllocations = allocations;
80     }
81 
82     /**
83      * Adds a stack call address for this allocation.
84      * @param address The address to add.
85      */
addStackCallAddress(long address)86     public void addStackCallAddress(long address) {
87         mStackCallAddresses.add(address);
88     }
89 
90     /**
91      * Returns the total size of this allocation.
92      */
getSize()93     public int getSize() {
94         return mSize;
95     }
96 
97     /**
98      * Returns whether the allocation happened in a child of the zygote
99      * process.
100      */
isZygoteChild()101     public boolean isZygoteChild() {
102         return mIsZygoteChild;
103     }
104 
105     /**
106      * Returns the allocation count.
107      */
getAllocationCount()108     public int getAllocationCount() {
109         return mAllocations;
110     }
111 
112     /**
113      * Returns whether the stack call addresses have been resolved into
114      * {@link NativeStackCallInfo} objects.
115      */
isStackCallResolved()116     public boolean isStackCallResolved() {
117         return mIsStackCallResolved;
118     }
119 
120     /**
121      * Returns the stack call of this allocation as raw addresses.
122      * @return the list of addresses where the allocation happened.
123      */
getStackCallAddresses()124     public List<Long> getStackCallAddresses() {
125         return mStackCallAddresses;
126     }
127 
128     /**
129      * Sets the resolved stack call for this allocation.
130      * <p/>
131      * If <code>resolvedStackCall</code> is non <code>null</code> then
132      * {@link #isStackCallResolved()} will return <code>true</code> after this call.
133      * @param resolvedStackCall The list of {@link NativeStackCallInfo}.
134      */
setResolvedStackCall(List<NativeStackCallInfo> resolvedStackCall)135     public synchronized void setResolvedStackCall(List<NativeStackCallInfo> resolvedStackCall) {
136         if (mResolvedStackCall == null) {
137             mResolvedStackCall = new ArrayList<NativeStackCallInfo>();
138         } else {
139             mResolvedStackCall.clear();
140         }
141         mResolvedStackCall.addAll(resolvedStackCall);
142         mIsStackCallResolved = mResolvedStackCall.size() != 0;
143     }
144 
145     /**
146      * Returns the resolved stack call.
147      * @return An array of {@link NativeStackCallInfo} or <code>null</code> if the stack call
148      * was not resolved.
149      * @see #setResolvedStackCall(ArrayList)
150      * @see #isStackCallResolved()
151      */
getResolvedStackCall()152     public synchronized List<NativeStackCallInfo> getResolvedStackCall() {
153         if (mIsStackCallResolved) {
154             return mResolvedStackCall;
155         }
156 
157         return null;
158     }
159 
160     /**
161      * Indicates whether some other object is "equal to" this one.
162      * @param obj the reference object with which to compare.
163      * @return <code>true</code> if this object is equal to the obj argument;
164      * <code>false</code> otherwise.
165      * @see java.lang.Object#equals(java.lang.Object)
166      */
167     @Override
equals(Object obj)168     public boolean equals(Object obj) {
169         if (obj == this)
170             return true;
171         if (obj instanceof NativeAllocationInfo) {
172             NativeAllocationInfo mi = (NativeAllocationInfo)obj;
173             // quick compare of size, alloc, and stackcall size
174             if (mSize != mi.mSize || mAllocations != mi.mAllocations ||
175                     mStackCallAddresses.size() != mi.mStackCallAddresses.size()) {
176                 return false;
177             }
178             // compare the stack addresses
179             int count = mStackCallAddresses.size();
180             for (int i = 0 ; i < count ; i++) {
181                 long a = mStackCallAddresses.get(i);
182                 long b = mi.mStackCallAddresses.get(i);
183                 if (a != b) {
184                     return false;
185                 }
186             }
187 
188             return true;
189         }
190         return false;
191     }
192 
193 
194     @Override
hashCode()195     public int hashCode() {
196         // Follow Effective Java's recipe re hash codes.
197         // Includes all the fields looked at by equals().
198 
199         int result = 17;    // arbitrary starting point
200 
201         result = 31 * result + mSize;
202         result = 31 * result + mAllocations;
203         result = 31 * result + mStackCallAddresses.size();
204 
205         for (long addr : mStackCallAddresses) {
206             result = 31 * result + (int) (addr ^ (addr >>> 32));
207         }
208 
209         return result;
210     }
211 
212     /**
213      * Returns a string representation of the object.
214      * @see java.lang.Object#toString()
215      */
216     @Override
toString()217     public String toString() {
218         StringBuffer buffer = new StringBuffer();
219         buffer.append(ALLOCATIONS_KW);
220         buffer.append(' ');
221         buffer.append(mAllocations);
222         buffer.append('\n');
223 
224         buffer.append(SIZE_KW);
225         buffer.append(' ');
226         buffer.append(mSize);
227         buffer.append('\n');
228 
229         buffer.append(TOTAL_SIZE_KW);
230         buffer.append(' ');
231         buffer.append(mSize * mAllocations);
232         buffer.append('\n');
233 
234         if (mResolvedStackCall != null) {
235             buffer.append(BEGIN_STACKTRACE_KW);
236             buffer.append('\n');
237             for (NativeStackCallInfo source : mResolvedStackCall) {
238                 long addr = source.getAddress();
239                 if (addr == 0) {
240                     continue;
241                 }
242 
243                 if (source.getLineNumber() != -1) {
244                     buffer.append(String.format("\t%1$08x\t%2$s --- %3$s --- %4$s:%5$d\n", addr,
245                             source.getLibraryName(), source.getMethodName(),
246                             source.getSourceFile(), source.getLineNumber()));
247                 } else {
248                     buffer.append(String.format("\t%1$08x\t%2$s --- %3$s --- %4$s\n", addr,
249                             source.getLibraryName(), source.getMethodName(), source.getSourceFile()));
250                 }
251             }
252             buffer.append(END_STACKTRACE_KW);
253             buffer.append('\n');
254         }
255 
256         return buffer.toString();
257     }
258 
259     /**
260      * Returns the first {@link NativeStackCallInfo} that is relevant.
261      * <p/>
262      * A relevant <code>NativeStackCallInfo</code> is a stack call that is not deep in the
263      * lower level of the libc, but the actual method that performed the allocation.
264      * @return a <code>NativeStackCallInfo</code> or <code>null</code> if the stack call has not
265      * been processed from the raw addresses.
266      * @see #setResolvedStackCall(ArrayList)
267      * @see #isStackCallResolved()
268      */
getRelevantStackCallInfo()269     public synchronized NativeStackCallInfo getRelevantStackCallInfo() {
270         if (mIsStackCallResolved && mResolvedStackCall != null) {
271             for (NativeStackCallInfo info : mResolvedStackCall) {
272                 if (isRelevantLibrary(info.getLibraryName())
273                         && isRelevantMethod(info.getMethodName())) {
274                     return info;
275                 }
276             }
277 
278             // couldnt find a relevant one, so we'll return the first one if it exists.
279             if (mResolvedStackCall.size() > 0)
280                 return mResolvedStackCall.get(0);
281         }
282 
283         return null;
284     }
285 
isRelevantLibrary(String libPath)286     private boolean isRelevantLibrary(String libPath) {
287         for (String l : FILTERED_LIBRARIES) {
288             if (libPath.endsWith(l)) {
289                 return false;
290             }
291         }
292 
293         return true;
294     }
295 
isRelevantMethod(String methodName)296     private boolean isRelevantMethod(String methodName) {
297         for (Pattern p : FILTERED_METHOD_NAME_PATTERNS) {
298             Matcher m = p.matcher(methodName);
299             if (m.find()) {
300                 return false;
301             }
302         }
303 
304         return true;
305     }
306 }
307