• 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 #include "alloc/HeapInternal.h"
20 
21 static HashTable *gStackTraceHashTable = NULL;
22 static int gSerialNumber = 0;
23 
24 /* Number of stack frames to cache */
25 #define STACK_DEPTH 8
26 
27 typedef struct {
28     int serialNumber;
29     int threadSerialNumber;
30     int frameIds[STACK_DEPTH];
31 } StackTrace;
32 
33 typedef struct {
34     StackTrace trace;
35     u1 live;
36 } StackTraceEntry;
37 
38 static u4 computeStackTraceHash(const StackTraceEntry *stackTraceEntry);
39 
40 int
hprofStartup_Stack()41 hprofStartup_Stack()
42 {
43     HashIter iter;
44 
45     /* This will be called when a GC begins. */
46     for (dvmHashIterBegin(gStackTraceHashTable, &iter);
47          !dvmHashIterDone(&iter);
48          dvmHashIterNext(&iter)) {
49         StackTraceEntry *stackTraceEntry;
50 
51         /* Clear the 'live' bit at the start of the GC pass. */
52         stackTraceEntry = (StackTraceEntry *) dvmHashIterData(&iter);
53         stackTraceEntry->live = 0;
54     }
55 
56     return 0;
57 }
58 
59 int
hprofShutdown_Stack()60 hprofShutdown_Stack()
61 {
62     HashIter iter;
63 
64     /* This will be called when a GC has completed. */
65     for (dvmHashIterBegin(gStackTraceHashTable, &iter);
66          !dvmHashIterDone(&iter);
67          dvmHashIterNext(&iter)) {
68         StackTraceEntry *stackTraceEntry;
69 
70         /*
71          * If the 'live' bit is 0, the trace is not in use by any current
72          * heap object and may be destroyed.
73          */
74         stackTraceEntry = (StackTraceEntry *) dvmHashIterData(&iter);
75         if (!stackTraceEntry->live) {
76             dvmHashTableRemove(gStackTraceHashTable,
77                     computeStackTraceHash(stackTraceEntry), stackTraceEntry);
78             free(stackTraceEntry);
79         }
80     }
81 
82     return 0;
83 }
84 
85 static u4
computeStackTraceHash(const StackTraceEntry * stackTraceEntry)86 computeStackTraceHash(const StackTraceEntry *stackTraceEntry)
87 {
88     u4 hash = 0;
89     const char *cp = (const char *) &stackTraceEntry->trace;
90     int i;
91 
92     for (i = 0; i < (int) sizeof(StackTrace); i++) {
93         hash = hash * 31 + cp[i];
94     }
95 
96     return hash;
97 }
98 
99 /* Only compare the 'trace' portion of the StackTraceEntry. */
100 static int
stackCmp(const void * tableItem,const void * looseItem)101 stackCmp(const void *tableItem, const void *looseItem)
102 {
103     return memcmp(&((StackTraceEntry *) tableItem)->trace,
104             &((StackTraceEntry *) looseItem)->trace, sizeof(StackTrace));
105 }
106 
107 static StackTraceEntry *
stackDup(const StackTraceEntry * stackTrace)108 stackDup(const StackTraceEntry *stackTrace)
109 {
110     StackTraceEntry *newStackTrace = malloc(sizeof(StackTraceEntry));
111     memcpy(newStackTrace, stackTrace, sizeof(StackTraceEntry));
112     return newStackTrace;
113 }
114 
115 static u4
hprofLookupStackSerialNumber(const StackTraceEntry * stackTrace)116 hprofLookupStackSerialNumber(const StackTraceEntry *stackTrace)
117 {
118     StackTraceEntry *val;
119     u4 hashValue;
120     int serial;
121 
122     /*
123      * Create the hash table on first contact.  We can't do this in
124      * hprofStartupStack, because we have to compute stack trace
125      * serial numbers and place them into object headers before the
126      * rest of hprof is triggered by a GC event.
127      */
128     if (gStackTraceHashTable == NULL) {
129         gStackTraceHashTable = dvmHashTableCreate(512, free);
130     }
131     dvmHashTableLock(gStackTraceHashTable);
132 
133     hashValue = computeStackTraceHash(stackTrace);
134     val = dvmHashTableLookup(gStackTraceHashTable, hashValue, (void *)stackTrace,
135             (HashCompareFunc)stackCmp, false);
136     if (val == NULL) {
137         StackTraceEntry *newStackTrace;
138 
139         newStackTrace = stackDup(stackTrace);
140         newStackTrace->trace.serialNumber = ++gSerialNumber;
141         val = dvmHashTableLookup(gStackTraceHashTable, hashValue,
142                 (void *)newStackTrace, (HashCompareFunc)stackCmp, true);
143         assert(val != NULL);
144     }
145 
146     /* Mark the trace as live (in use by an object in the current heap). */
147     val->live = 1;
148 
149     /* Grab the serial number before unlocking the table. */
150     serial = val->trace.serialNumber;
151 
152     dvmHashTableUnlock(gStackTraceHashTable);
153 
154     return serial;
155 }
156 
157 int
hprofDumpStacks(hprof_context_t * ctx)158 hprofDumpStacks(hprof_context_t *ctx)
159 {
160     HashIter iter;
161     hprof_record_t *rec = &ctx->curRec;
162 
163     dvmHashTableLock(gStackTraceHashTable);
164 
165     for (dvmHashIterBegin(gStackTraceHashTable, &iter);
166          !dvmHashIterDone(&iter);
167          dvmHashIterNext(&iter))
168     {
169         const StackTraceEntry *stackTraceEntry;
170         int count;
171         int i;
172 
173         hprofStartNewRecord(ctx, HPROF_TAG_STACK_TRACE, HPROF_TIME);
174 
175         stackTraceEntry = (const StackTraceEntry *) dvmHashIterData(&iter);
176         assert(stackTraceEntry != NULL);
177 
178         /* STACK TRACE format:
179          *
180          * u4:     serial number for this stack
181          * u4:     serial number for the running thread
182          * u4:     number of frames
183          * [ID]*:  ID for the stack frame
184          */
185         hprofAddU4ToRecord(rec, stackTraceEntry->trace.serialNumber);
186         hprofAddU4ToRecord(rec, stackTraceEntry->trace.threadSerialNumber);
187 
188         count = 0;
189         while ((count < STACK_DEPTH) &&
190                (stackTraceEntry->trace.frameIds[count] != 0)) {
191             count++;
192         }
193         hprofAddU4ToRecord(rec, count);
194         for (i = 0; i < count; i++) {
195             hprofAddU4ToRecord(rec, stackTraceEntry->trace.frameIds[i]);
196         }
197     }
198 
199     dvmHashTableUnlock(gStackTraceHashTable);
200 
201     return 0;
202 }
203 
204 void
hprofFillInStackTrace(void * objectPtr)205 hprofFillInStackTrace(void *objectPtr)
206 
207 {
208     DvmHeapChunk *chunk;
209     StackTraceEntry stackTraceEntry;
210     Thread* self;
211     void* fp;
212     int i;
213 
214     if (objectPtr == NULL) {
215         return;
216     }
217     self = dvmThreadSelf();
218     if (self == NULL) {
219         return;
220     }
221     fp = self->curFrame;
222 
223     /* Serial number to be filled in later. */
224     stackTraceEntry.trace.serialNumber = -1;
225 
226     /*
227      * TODO - The HAT tool doesn't care about thread data, so we can defer
228      * actually emitting thread records and assigning thread serial numbers.
229      */
230     stackTraceEntry.trace.threadSerialNumber = (int) self;
231 
232     memset(&stackTraceEntry.trace.frameIds, 0,
233             sizeof(stackTraceEntry.trace.frameIds));
234 
235     i = 0;
236     while ((fp != NULL) && (i < STACK_DEPTH)) {
237         const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
238         const Method* method = saveArea->method;
239         StackFrameEntry frame;
240 
241         if (!dvmIsBreakFrame(fp)) {
242             frame.frame.method = method;
243             if (dvmIsNativeMethod(method)) {
244                 frame.frame.pc = 0; /* no saved PC for native methods */
245             } else {
246                 assert(saveArea->xtra.currentPc >= method->insns &&
247                         saveArea->xtra.currentPc <
248                         method->insns + dvmGetMethodInsnsSize(method));
249                 frame.frame.pc = (int) (saveArea->xtra.currentPc -
250                         method->insns);
251             }
252 
253             // Canonicalize the frame and cache it in the hprof context
254             stackTraceEntry.trace.frameIds[i++] =
255                 hprofLookupStackFrameId(&frame);
256         }
257 
258         assert(fp != saveArea->prevFrame);
259         fp = saveArea->prevFrame;
260     }
261 
262     /* Store the stack trace serial number in the object header */
263     chunk = ptr2chunk(objectPtr);
264     chunk->stackTraceSerialNumber =
265             hprofLookupStackSerialNumber(&stackTraceEntry);
266 }
267