| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "Hprof.h" |
| #include "HprofStack.h" |
| |
| #include "alloc/HeapInternal.h" |
| |
| static HashTable *gStackFrameHashTable; |
| |
| static u4 computeStackFrameHash(const StackFrameEntry *stackFrameEntry); |
| |
| int |
| hprofStartup_StackFrame() |
| { |
| HashIter iter; |
| |
| /* Cache the string "<unknown>" for use when the source file is |
| * unknown. |
| */ |
| hprofLookupStringId("<unknown>"); |
| |
| /* This will be called when a GC begins. */ |
| for (dvmHashIterBegin(gStackFrameHashTable, &iter); |
| !dvmHashIterDone(&iter); |
| dvmHashIterNext(&iter)) { |
| StackFrameEntry *stackFrameEntry; |
| const Method *method; |
| |
| /* Clear the 'live' bit at the start of the GC pass. */ |
| stackFrameEntry = (StackFrameEntry *) dvmHashIterData(&iter); |
| stackFrameEntry->live = 0; |
| |
| method = stackFrameEntry->frame.method; |
| if (method == NULL) { |
| continue; |
| } |
| |
| /* Make sure the method name, descriptor, and source file are in |
| * the string table, and that the method class is in the class |
| * table. This is needed because strings and classes will be dumped |
| * before stack frames. |
| */ |
| |
| if (method->name) { |
| hprofLookupStringId(method->name); |
| } |
| |
| DexStringCache cache; |
| const char* descriptor; |
| |
| dexStringCacheInit(&cache); |
| descriptor = dexProtoGetMethodDescriptor(&method->prototype, &cache); |
| hprofLookupStringId(descriptor); |
| dexStringCacheRelease(&cache); |
| |
| const char* sourceFile = dvmGetMethodSourceFile(method); |
| if (sourceFile) { |
| hprofLookupStringId(sourceFile); |
| } |
| |
| if (method->clazz) { |
| hprofLookupClassId(method->clazz); |
| } |
| } |
| |
| return 0; |
| } |
| |
| int |
| hprofShutdown_StackFrame() |
| { |
| HashIter iter; |
| |
| /* This will be called when a GC has completed. */ |
| for (dvmHashIterBegin(gStackFrameHashTable, &iter); |
| !dvmHashIterDone(&iter); |
| dvmHashIterNext(&iter)) { |
| const StackFrameEntry *stackFrameEntry; |
| |
| /* |
| * If the 'live' bit is 0, the frame is not in use by any current |
| * heap object and may be destroyed. |
| */ |
| stackFrameEntry = (const StackFrameEntry *) dvmHashIterData(&iter); |
| if (!stackFrameEntry->live) { |
| dvmHashTableRemove(gStackFrameHashTable, |
| computeStackFrameHash(stackFrameEntry), |
| (void*) stackFrameEntry); |
| free((void*) stackFrameEntry); |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* Only hash the 'frame' portion of the StackFrameEntry. */ |
| static u4 |
| computeStackFrameHash(const StackFrameEntry *stackFrameEntry) |
| { |
| u4 hash = 0; |
| const char *cp = (char *) &stackFrameEntry->frame; |
| int i; |
| |
| for (i = 0; i < (int) sizeof(StackFrame); i++) { |
| hash = 31 * hash + cp[i]; |
| } |
| return hash; |
| } |
| |
| /* Only compare the 'frame' portion of the StackFrameEntry. */ |
| static int |
| stackFrameCmp(const void *tableItem, const void *looseItem) |
| { |
| return memcmp(&((StackFrameEntry *)tableItem)->frame, |
| &((StackFrameEntry *) looseItem)->frame, sizeof(StackFrame)); |
| } |
| |
| static StackFrameEntry * |
| stackFrameDup(const StackFrameEntry *stackFrameEntry) |
| { |
| StackFrameEntry *newStackFrameEntry = malloc(sizeof(StackFrameEntry)); |
| memcpy(newStackFrameEntry, stackFrameEntry, sizeof(StackFrameEntry)); |
| return newStackFrameEntry; |
| } |
| |
| hprof_stack_frame_id |
| hprofLookupStackFrameId(const StackFrameEntry *stackFrameEntry) |
| { |
| StackFrameEntry *val; |
| u4 hashValue; |
| |
| /* |
| * Create the hash table on first contact. We can't do this in |
| * hprofStartupStackFrame, because we have to compute stack trace |
| * serial numbers and place them into object headers before the |
| * rest of hprof is triggered by a GC event. |
| */ |
| if (gStackFrameHashTable == NULL) { |
| gStackFrameHashTable = dvmHashTableCreate(512, free); |
| } |
| dvmHashTableLock(gStackFrameHashTable); |
| |
| hashValue = computeStackFrameHash(stackFrameEntry); |
| val = dvmHashTableLookup(gStackFrameHashTable, hashValue, |
| (void *)stackFrameEntry, (HashCompareFunc)stackFrameCmp, false); |
| if (val == NULL) { |
| const StackFrameEntry *newStackFrameEntry; |
| |
| newStackFrameEntry = stackFrameDup(stackFrameEntry); |
| val = dvmHashTableLookup(gStackFrameHashTable, hashValue, |
| (void *)newStackFrameEntry, (HashCompareFunc)stackFrameCmp, true); |
| assert(val != NULL); |
| } |
| |
| /* Mark the frame as live (in use by an object in the current heap). */ |
| val->live = 1; |
| |
| dvmHashTableUnlock(gStackFrameHashTable); |
| |
| return (hprof_stack_frame_id) val; |
| } |
| |
| int |
| hprofDumpStackFrames(hprof_context_t *ctx) |
| { |
| HashIter iter; |
| hprof_record_t *rec = &ctx->curRec; |
| |
| dvmHashTableLock(gStackFrameHashTable); |
| |
| for (dvmHashIterBegin(gStackFrameHashTable, &iter); |
| !dvmHashIterDone(&iter); |
| dvmHashIterNext(&iter)) |
| { |
| const StackFrameEntry *stackFrameEntry; |
| const Method *method; |
| int pc; |
| const char *sourceFile; |
| ClassObject *clazz; |
| int lineNum; |
| |
| hprofStartNewRecord(ctx, HPROF_TAG_STACK_FRAME, HPROF_TIME); |
| |
| stackFrameEntry = (const StackFrameEntry *) dvmHashIterData(&iter); |
| assert(stackFrameEntry != NULL); |
| |
| method = stackFrameEntry->frame.method; |
| pc = stackFrameEntry->frame.pc; |
| sourceFile = dvmGetMethodSourceFile(method); |
| if (sourceFile == NULL) { |
| sourceFile = "<unknown>"; |
| lineNum = 0; |
| } else { |
| lineNum = dvmLineNumFromPC(method, pc); |
| } |
| clazz = (ClassObject *) hprofLookupClassId(method->clazz); |
| |
| /* STACK FRAME format: |
| * |
| * ID: ID for this stack frame |
| * ID: ID for the method name |
| * ID: ID for the method descriptor |
| * ID: ID for the source file name |
| * u4: class serial number |
| * u4: line number, 0 = no line information |
| * |
| * We use the address of the stack frame as its ID. |
| */ |
| |
| DexStringCache cache; |
| const char* descriptor; |
| |
| dexStringCacheInit(&cache); |
| descriptor = dexProtoGetMethodDescriptor(&method->prototype, &cache); |
| |
| hprofAddIdToRecord(rec, (u4) stackFrameEntry); |
| hprofAddIdToRecord(rec, hprofLookupStringId(method->name)); |
| hprofAddIdToRecord(rec, hprofLookupStringId(descriptor)); |
| hprofAddIdToRecord(rec, hprofLookupStringId(sourceFile)); |
| hprofAddU4ToRecord(rec, (u4) clazz->hprofSerialNumber); |
| hprofAddU4ToRecord(rec, (u4) lineNum); |
| |
| dexStringCacheRelease(&cache); |
| } |
| |
| dvmHashTableUnlock(gStackFrameHashTable); |
| return 0; |
| } |