/* * 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->serialNumber); hprofAddU4ToRecord(rec, (u4) lineNum); dexStringCacheRelease(&cache); } dvmHashTableUnlock(gStackFrameHashTable); return 0; }