Home | History | Annotate | Download | only in hprof
      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