• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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 #include "Hprof.h"
18 #include "HprofStack.h"
19 
20 #include "alloc/HeapInternal.h"
21 
22 static HashTable *gStackFrameHashTable;
23 
24 static u4 computeStackFrameHash(const StackFrameEntry *stackFrameEntry);
25 
26 int
hprofStartup_StackFrame()27 hprofStartup_StackFrame()
28 {
29     HashIter iter;
30 
31     /* Cache the string "<unknown>" for use when the source file is
32      * unknown.
33      */
34     hprofLookupStringId("<unknown>");
35 
36     /* This will be called when a GC begins. */
37     for (dvmHashIterBegin(gStackFrameHashTable, &iter);
38          !dvmHashIterDone(&iter);
39          dvmHashIterNext(&iter)) {
40         StackFrameEntry *stackFrameEntry;
41         const Method *method;
42 
43         /* Clear the 'live' bit at the start of the GC pass. */
44         stackFrameEntry = (StackFrameEntry *) dvmHashIterData(&iter);
45         stackFrameEntry->live = 0;
46 
47         method = stackFrameEntry->frame.method;
48         if (method == NULL) {
49             continue;
50         }
51 
52         /* Make sure the method name, descriptor, and source file are in
53          * the string table, and that the method class is in the class
54          * table. This is needed because strings and classes will be dumped
55          * before stack frames.
56          */
57 
58         if (method->name) {
59             hprofLookupStringId(method->name);
60         }
61 
62         DexStringCache cache;
63         const char* descriptor;
64 
65         dexStringCacheInit(&cache);
66         descriptor = dexProtoGetMethodDescriptor(&method->prototype, &cache);
67         hprofLookupStringId(descriptor);
68         dexStringCacheRelease(&cache);
69 
70         const char* sourceFile = dvmGetMethodSourceFile(method);
71         if (sourceFile) {
72             hprofLookupStringId(sourceFile);
73         }
74 
75         if (method->clazz) {
76             hprofLookupClassId(method->clazz);
77         }
78     }
79 
80     return 0;
81 }
82 
83 int
hprofShutdown_StackFrame()84 hprofShutdown_StackFrame()
85 {
86     HashIter iter;
87 
88     /* This will be called when a GC has completed. */
89     for (dvmHashIterBegin(gStackFrameHashTable, &iter);
90          !dvmHashIterDone(&iter);
91          dvmHashIterNext(&iter)) {
92         const StackFrameEntry *stackFrameEntry;
93 
94         /*
95          * If the 'live' bit is 0, the frame is not in use by any current
96          * heap object and may be destroyed.
97          */
98         stackFrameEntry = (const StackFrameEntry *) dvmHashIterData(&iter);
99         if (!stackFrameEntry->live) {
100             dvmHashTableRemove(gStackFrameHashTable,
101                     computeStackFrameHash(stackFrameEntry),
102                     (void*) stackFrameEntry);
103             free((void*) stackFrameEntry);
104         }
105     }
106 
107     return 0;
108 }
109 
110 /* Only hash the 'frame' portion of the StackFrameEntry. */
111 static u4
computeStackFrameHash(const StackFrameEntry * stackFrameEntry)112 computeStackFrameHash(const StackFrameEntry *stackFrameEntry)
113 {
114     u4 hash = 0;
115     const char *cp = (char *) &stackFrameEntry->frame;
116     int i;
117 
118     for (i = 0; i < (int) sizeof(StackFrame); i++) {
119         hash = 31 * hash + cp[i];
120     }
121     return hash;
122 }
123 
124 /* Only compare the 'frame' portion of the StackFrameEntry. */
125 static int
stackFrameCmp(const void * tableItem,const void * looseItem)126 stackFrameCmp(const void *tableItem, const void *looseItem)
127 {
128     return memcmp(&((StackFrameEntry *)tableItem)->frame,
129             &((StackFrameEntry *) looseItem)->frame, sizeof(StackFrame));
130 }
131 
132 static StackFrameEntry *
stackFrameDup(const StackFrameEntry * stackFrameEntry)133 stackFrameDup(const StackFrameEntry *stackFrameEntry)
134 {
135     StackFrameEntry *newStackFrameEntry = malloc(sizeof(StackFrameEntry));
136     memcpy(newStackFrameEntry, stackFrameEntry, sizeof(StackFrameEntry));
137     return newStackFrameEntry;
138 }
139 
140 hprof_stack_frame_id
hprofLookupStackFrameId(const StackFrameEntry * stackFrameEntry)141 hprofLookupStackFrameId(const StackFrameEntry *stackFrameEntry)
142 {
143     StackFrameEntry *val;
144     u4 hashValue;
145 
146     /*
147      * Create the hash table on first contact.  We can't do this in
148      * hprofStartupStackFrame, because we have to compute stack trace
149      * serial numbers and place them into object headers before the
150      * rest of hprof is triggered by a GC event.
151      */
152     if (gStackFrameHashTable == NULL) {
153         gStackFrameHashTable = dvmHashTableCreate(512, free);
154     }
155     dvmHashTableLock(gStackFrameHashTable);
156 
157     hashValue = computeStackFrameHash(stackFrameEntry);
158     val = dvmHashTableLookup(gStackFrameHashTable, hashValue,
159         (void *)stackFrameEntry, (HashCompareFunc)stackFrameCmp, false);
160     if (val == NULL) {
161         const StackFrameEntry *newStackFrameEntry;
162 
163         newStackFrameEntry = stackFrameDup(stackFrameEntry);
164         val = dvmHashTableLookup(gStackFrameHashTable, hashValue,
165             (void *)newStackFrameEntry, (HashCompareFunc)stackFrameCmp, true);
166         assert(val != NULL);
167     }
168 
169     /* Mark the frame as live (in use by an object in the current heap). */
170     val->live = 1;
171 
172     dvmHashTableUnlock(gStackFrameHashTable);
173 
174     return (hprof_stack_frame_id) val;
175 }
176 
177 int
hprofDumpStackFrames(hprof_context_t * ctx)178 hprofDumpStackFrames(hprof_context_t *ctx)
179 {
180     HashIter iter;
181     hprof_record_t *rec = &ctx->curRec;
182 
183     dvmHashTableLock(gStackFrameHashTable);
184 
185     for (dvmHashIterBegin(gStackFrameHashTable, &iter);
186          !dvmHashIterDone(&iter);
187          dvmHashIterNext(&iter))
188     {
189         const StackFrameEntry *stackFrameEntry;
190         const Method *method;
191         int pc;
192         const char *sourceFile;
193         ClassObject *clazz;
194         int lineNum;
195 
196         hprofStartNewRecord(ctx, HPROF_TAG_STACK_FRAME, HPROF_TIME);
197 
198         stackFrameEntry = (const StackFrameEntry *) dvmHashIterData(&iter);
199         assert(stackFrameEntry != NULL);
200 
201         method = stackFrameEntry->frame.method;
202         pc = stackFrameEntry->frame.pc;
203         sourceFile = dvmGetMethodSourceFile(method);
204         if (sourceFile == NULL) {
205             sourceFile = "<unknown>";
206             lineNum = 0;
207         } else {
208             lineNum = dvmLineNumFromPC(method, pc);
209         }
210         clazz = (ClassObject *) hprofLookupClassId(method->clazz);
211 
212         /* STACK FRAME format:
213          *
214          * ID:     ID for this stack frame
215          * ID:     ID for the method name
216          * ID:     ID for the method descriptor
217          * ID:     ID for the source file name
218          * u4:     class serial number
219          * u4:     line number, 0 = no line information
220          *
221          * We use the address of the stack frame as its ID.
222          */
223 
224         DexStringCache cache;
225         const char* descriptor;
226 
227         dexStringCacheInit(&cache);
228         descriptor = dexProtoGetMethodDescriptor(&method->prototype, &cache);
229 
230         hprofAddIdToRecord(rec, (u4) stackFrameEntry);
231         hprofAddIdToRecord(rec, hprofLookupStringId(method->name));
232         hprofAddIdToRecord(rec, hprofLookupStringId(descriptor));
233         hprofAddIdToRecord(rec, hprofLookupStringId(sourceFile));
234         hprofAddU4ToRecord(rec, (u4) clazz->serialNumber);
235         hprofAddU4ToRecord(rec, (u4) lineNum);
236 
237         dexStringCacheRelease(&cache);
238     }
239 
240     dvmHashTableUnlock(gStackFrameHashTable);
241     return 0;
242 }
243