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