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 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 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 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 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 * 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 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 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 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