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 * Class object pool
18 */
19
20#include "Hprof.h"
21
22static HashTable *gClassHashTable;
23
24int
25hprofStartup_Class()
26{
27    gClassHashTable = dvmHashTableCreate(128, NULL);
28    if (gClassHashTable == NULL) {
29        return UNIQUE_ERROR();
30    }
31    return 0;
32}
33
34int
35hprofShutdown_Class()
36{
37    dvmHashTableFree(gClassHashTable);
38
39    return 0;
40}
41
42static u4
43computeClassHash(const ClassObject *clazz)
44{
45    u4 hash;
46    const char *cp;
47    char c;
48
49    cp = clazz->descriptor;
50    hash = (u4)clazz->classLoader;
51    while ((c = *cp++) != '\0') {
52        hash = hash * 31 + c;
53    }
54
55    return hash;
56}
57
58static int
59classCmp(const void *v1, const void *v2)
60{
61    const ClassObject *c1 = (const ClassObject *)v1;
62    const ClassObject *c2 = (const ClassObject *)v2;
63    intptr_t diff;
64
65    diff = (uintptr_t)c1->classLoader - (uintptr_t)c2->classLoader;
66    if (diff == 0) {
67        return strcmp(c1->descriptor, c2->descriptor);
68    }
69    return diff;
70}
71
72static int
73getPrettyClassNameId(const char *descriptor)
74{
75    hprof_string_id classNameId;
76    char *dotName = dvmDescriptorToDot(descriptor);
77
78    /* Hprof suggests that array class names be converted from, e.g.,
79     * "[[[I" to "int[][][]" and "[Lorg.blort.Spaz;" to
80     * "org.blort.Spaz[]".
81     */
82    if (dotName[0] == '[') {
83        const char *c;
84        char *newName;
85        char *nc;
86        size_t dim;
87        size_t newLen;
88
89        c = dotName;
90        dim = 0;
91        while (*c == '[') {
92            dim++;
93            c++;
94        }
95        if (*c == 'L') {
96            c++;
97        } else {
98            /* It's a primitive type;  we should use a pretty name.
99             * Add semicolons to make all strings have the format
100             * of object class names.
101             */
102            switch (*c) {
103            case 'Z': c = "boolean;";    break;
104            case 'C': c = "char;";       break;
105            case 'F': c = "float;";      break;
106            case 'D': c = "double;";     break;
107            case 'B': c = "byte;";       break;
108            case 'S': c = "short;";      break;
109            case 'I': c = "int;";        break;
110            case 'J': c = "long;";       break;
111            default: assert(false); c = "UNKNOWN;"; break;
112            }
113        }
114
115        /* We have a string of the form "name;" and
116         * we want to replace the semicolon with as many
117         * "[]" pairs as is in dim.
118         */
119        newLen = strlen(c)-1 + dim*2;
120        newName = malloc(newLen + 1);
121        if (newName == NULL) {
122            return -1;
123        }
124        strcpy(newName, c);
125        newName[newLen] = '\0';
126
127        /* Point nc to the semicolon.
128         */
129        nc = newName + newLen - dim*2;
130        assert(*nc == ';');
131
132        while (dim--) {
133            *nc++ = '[';
134            *nc++ = ']';
135        }
136        assert(*nc == '\0');
137
138        classNameId = hprofLookupStringId(newName);
139        free(newName);
140    } else {
141        classNameId = hprofLookupStringId(dotName);
142    }
143
144    free(dotName);
145    return classNameId;
146}
147
148
149hprof_class_object_id
150hprofLookupClassId(const ClassObject *clazz)
151{
152    void *val;
153
154    if (clazz == NULL) {
155        /* Someone's probably looking up the superclass
156         * of java.lang.Object or of a primitive class.
157         */
158        return (hprof_class_object_id)0;
159    }
160
161    dvmHashTableLock(gClassHashTable);
162
163    /* We're using the hash table as a list.
164     * TODO: replace the hash table with a more suitable structure
165     */
166    val = dvmHashTableLookup(gClassHashTable, computeClassHash(clazz),
167            (void *)clazz, classCmp, true);
168    assert(val != NULL);
169
170    dvmHashTableUnlock(gClassHashTable);
171
172    /* Make sure that the class's name is in the string table.
173     * This is a bunch of extra work that we only have to do
174     * because of the order of tables in the output file
175     * (strings need to be dumped before classes).
176     */
177    getPrettyClassNameId(clazz->descriptor);
178
179    return (hprof_class_object_id)clazz;
180}
181
182int
183hprofDumpClasses(hprof_context_t *ctx)
184{
185    HashIter iter;
186    hprof_record_t *rec = &ctx->curRec;
187    int err;
188
189    dvmHashTableLock(gClassHashTable);
190
191    for (err = 0, dvmHashIterBegin(gClassHashTable, &iter);
192         err == 0 && !dvmHashIterDone(&iter);
193         dvmHashIterNext(&iter))
194    {
195        err = hprofStartNewRecord(ctx, HPROF_TAG_LOAD_CLASS, HPROF_TIME);
196        if (err == 0) {
197            const ClassObject *clazz;
198
199            clazz = (const ClassObject *)dvmHashIterData(&iter);
200            assert(clazz != NULL);
201
202            /* LOAD CLASS format:
203             *
204             * u4:     class serial number (always > 0)
205             * ID:     class object ID
206             * u4:     stack trace serial number
207             * ID:     class name string ID
208             *
209             * We use the address of the class object structure as its ID.
210             */
211            hprofAddU4ToRecord(rec, clazz->serialNumber);
212            hprofAddIdToRecord(rec, (hprof_class_object_id)clazz);
213            hprofAddU4ToRecord(rec, HPROF_NULL_STACK_TRACE);
214            hprofAddIdToRecord(rec, getPrettyClassNameId(clazz->descriptor));
215        }
216    }
217
218    dvmHashTableUnlock(gClassHashTable);
219
220    return err;
221}
222