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
22static HashTable *gStackFrameHashTable;
23
24static u4 computeStackFrameHash(const StackFrameEntry *stackFrameEntry);
25
26int
27hprofStartup_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
83int
84hprofShutdown_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. */
111static u4
112computeStackFrameHash(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. */
125static int
126stackFrameCmp(const void *tableItem, const void *looseItem)
127{
128    return memcmp(&((StackFrameEntry *)tableItem)->frame,
129            &((StackFrameEntry *) looseItem)->frame, sizeof(StackFrame));
130}
131
132static StackFrameEntry *
133stackFrameDup(const StackFrameEntry *stackFrameEntry)
134{
135    StackFrameEntry *newStackFrameEntry = malloc(sizeof(StackFrameEntry));
136    memcpy(newStackFrameEntry, stackFrameEntry, sizeof(StackFrameEntry));
137    return newStackFrameEntry;
138}
139
140hprof_stack_frame_id
141hprofLookupStackFrameId(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
177int
178hprofDumpStackFrames(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