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
22 static HashTable *gClassHashTable;
23
24 int
hprofStartup_Class()25 hprofStartup_Class()
26 {
27 gClassHashTable = dvmHashTableCreate(128, NULL);
28 if (gClassHashTable == NULL) {
29 return UNIQUE_ERROR();
30 }
31 return 0;
32 }
33
34 int
hprofShutdown_Class()35 hprofShutdown_Class()
36 {
37 dvmHashTableFree(gClassHashTable);
38
39 return 0;
40 }
41
42 static u4
computeClassHash(const ClassObject * clazz)43 computeClassHash(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
58 static int
classCmp(const void * v1,const void * v2)59 classCmp(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
72 static int
getPrettyClassNameId(const char * descriptor)73 getPrettyClassNameId(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
149 hprof_class_object_id
hprofLookupClassId(const ClassObject * clazz)150 hprofLookupClassId(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
182 int
hprofDumpClasses(hprof_context_t * ctx)183 hprofDumpClasses(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