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