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