/* * 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 *gStackTraceHashTable = NULL; static int gSerialNumber = 0; /* Number of stack frames to cache */ #define STACK_DEPTH 8 typedef struct { int serialNumber; int threadSerialNumber; int frameIds[STACK_DEPTH]; } StackTrace; typedef struct { StackTrace trace; u1 live; } StackTraceEntry; static u4 computeStackTraceHash(const StackTraceEntry *stackTraceEntry); int hprofStartup_Stack() { HashIter iter; /* This will be called when a GC begins. */ for (dvmHashIterBegin(gStackTraceHashTable, &iter); !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { StackTraceEntry *stackTraceEntry; /* Clear the 'live' bit at the start of the GC pass. */ stackTraceEntry = (StackTraceEntry *) dvmHashIterData(&iter); stackTraceEntry->live = 0; } return 0; } int hprofShutdown_Stack() { HashIter iter; /* This will be called when a GC has completed. */ for (dvmHashIterBegin(gStackTraceHashTable, &iter); !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { StackTraceEntry *stackTraceEntry; /* * If the 'live' bit is 0, the trace is not in use by any current * heap object and may be destroyed. */ stackTraceEntry = (StackTraceEntry *) dvmHashIterData(&iter); if (!stackTraceEntry->live) { dvmHashTableRemove(gStackTraceHashTable, computeStackTraceHash(stackTraceEntry), stackTraceEntry); free(stackTraceEntry); } } return 0; } static u4 computeStackTraceHash(const StackTraceEntry *stackTraceEntry) { u4 hash = 0; const char *cp = (const char *) &stackTraceEntry->trace; int i; for (i = 0; i < (int) sizeof(StackTrace); i++) { hash = hash * 31 + cp[i]; } return hash; } /* Only compare the 'trace' portion of the StackTraceEntry. */ static int stackCmp(const void *tableItem, const void *looseItem) { return memcmp(&((StackTraceEntry *) tableItem)->trace, &((StackTraceEntry *) looseItem)->trace, sizeof(StackTrace)); } static StackTraceEntry * stackDup(const StackTraceEntry *stackTrace) { StackTraceEntry *newStackTrace = malloc(sizeof(StackTraceEntry)); memcpy(newStackTrace, stackTrace, sizeof(StackTraceEntry)); return newStackTrace; } static u4 hprofLookupStackSerialNumber(const StackTraceEntry *stackTrace) { StackTraceEntry *val; u4 hashValue; int serial; /* * Create the hash table on first contact. We can't do this in * hprofStartupStack, 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 (gStackTraceHashTable == NULL) { gStackTraceHashTable = dvmHashTableCreate(512, free); } dvmHashTableLock(gStackTraceHashTable); hashValue = computeStackTraceHash(stackTrace); val = dvmHashTableLookup(gStackTraceHashTable, hashValue, (void *)stackTrace, (HashCompareFunc)stackCmp, false); if (val == NULL) { StackTraceEntry *newStackTrace; newStackTrace = stackDup(stackTrace); newStackTrace->trace.serialNumber = ++gSerialNumber; val = dvmHashTableLookup(gStackTraceHashTable, hashValue, (void *)newStackTrace, (HashCompareFunc)stackCmp, true); assert(val != NULL); } /* Mark the trace as live (in use by an object in the current heap). */ val->live = 1; /* Grab the serial number before unlocking the table. */ serial = val->trace.serialNumber; dvmHashTableUnlock(gStackTraceHashTable); return serial; } int hprofDumpStacks(hprof_context_t *ctx) { HashIter iter; hprof_record_t *rec = &ctx->curRec; dvmHashTableLock(gStackTraceHashTable); for (dvmHashIterBegin(gStackTraceHashTable, &iter); !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { const StackTraceEntry *stackTraceEntry; int count; int i; hprofStartNewRecord(ctx, HPROF_TAG_STACK_TRACE, HPROF_TIME); stackTraceEntry = (const StackTraceEntry *) dvmHashIterData(&iter); assert(stackTraceEntry != NULL); /* STACK TRACE format: * * u4: serial number for this stack * u4: serial number for the running thread * u4: number of frames * [ID]*: ID for the stack frame */ hprofAddU4ToRecord(rec, stackTraceEntry->trace.serialNumber); hprofAddU4ToRecord(rec, stackTraceEntry->trace.threadSerialNumber); count = 0; while ((count < STACK_DEPTH) && (stackTraceEntry->trace.frameIds[count] != 0)) { count++; } hprofAddU4ToRecord(rec, count); for (i = 0; i < count; i++) { hprofAddU4ToRecord(rec, stackTraceEntry->trace.frameIds[i]); } } dvmHashTableUnlock(gStackTraceHashTable); return 0; } void hprofFillInStackTrace(void *objectPtr) { DvmHeapChunk *chunk; StackTraceEntry stackTraceEntry; Thread* self; void* fp; int i; if (objectPtr == NULL) { return; } self = dvmThreadSelf(); if (self == NULL) { return; } fp = self->curFrame; /* Serial number to be filled in later. */ stackTraceEntry.trace.serialNumber = -1; /* * TODO - The HAT tool doesn't care about thread data, so we can defer * actually emitting thread records and assigning thread serial numbers. */ stackTraceEntry.trace.threadSerialNumber = (int) self; memset(&stackTraceEntry.trace.frameIds, 0, sizeof(stackTraceEntry.trace.frameIds)); i = 0; while ((fp != NULL) && (i < STACK_DEPTH)) { const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); const Method* method = saveArea->method; StackFrameEntry frame; if (!dvmIsBreakFrame(fp)) { frame.frame.method = method; if (dvmIsNativeMethod(method)) { frame.frame.pc = 0; /* no saved PC for native methods */ } else { assert(saveArea->xtra.currentPc >= method->insns && saveArea->xtra.currentPc < method->insns + dvmGetMethodInsnsSize(method)); frame.frame.pc = (int) (saveArea->xtra.currentPc - method->insns); } // Canonicalize the frame and cache it in the hprof context stackTraceEntry.trace.frameIds[i++] = hprofLookupStackFrameId(&frame); } assert(fp != saveArea->prevFrame); fp = saveArea->prevFrame; } /* Store the stack trace serial number in the object header */ chunk = ptr2chunk(objectPtr); chunk->stackTraceSerialNumber = hprofLookupStackSerialNumber(&stackTraceEntry); }