/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Class object pool */ #include "Hprof.h" static HashTable *gClassHashTable; int hprofStartup_Class() { gClassHashTable = dvmHashTableCreate(128, NULL); if (gClassHashTable == NULL) { return UNIQUE_ERROR(); } return 0; } int hprofShutdown_Class() { dvmHashTableFree(gClassHashTable); return 0; } static u4 computeClassHash(const ClassObject *clazz) { u4 hash; const char *cp; char c; cp = clazz->descriptor; hash = (u4)clazz->classLoader; while ((c = *cp++) != '\0') { hash = hash * 31 + c; } return hash; } static int classCmp(const void *v1, const void *v2) { const ClassObject *c1 = (const ClassObject *)v1; const ClassObject *c2 = (const ClassObject *)v2; intptr_t diff; diff = (uintptr_t)c1->classLoader - (uintptr_t)c2->classLoader; if (diff == 0) { return strcmp(c1->descriptor, c2->descriptor); } return diff; } static int getPrettyClassNameId(const char *descriptor) { hprof_string_id classNameId; char *dotName = dvmDescriptorToDot(descriptor); /* Hprof suggests that array class names be converted from, e.g., * "[[[I" to "int[][][]" and "[Lorg.blort.Spaz;" to * "org.blort.Spaz[]". */ if (dotName[0] == '[') { const char *c; char *newName; char *nc; size_t dim; size_t newLen; c = dotName; dim = 0; while (*c == '[') { dim++; c++; } if (*c == 'L') { c++; } else { /* It's a primitive type; we should use a pretty name. * Add semicolons to make all strings have the format * of object class names. */ switch (*c) { case 'Z': c = "boolean;"; break; case 'C': c = "char;"; break; case 'F': c = "float;"; break; case 'D': c = "double;"; break; case 'B': c = "byte;"; break; case 'S': c = "short;"; break; case 'I': c = "int;"; break; case 'J': c = "long;"; break; default: assert(false); c = "UNKNOWN;"; break; } } /* We have a string of the form "name;" and * we want to replace the semicolon with as many * "[]" pairs as is in dim. */ newLen = strlen(c)-1 + dim*2; newName = malloc(newLen + 1); if (newName == NULL) { return -1; } strcpy(newName, c); newName[newLen] = '\0'; /* Point nc to the semicolon. */ nc = newName + newLen - dim*2; assert(*nc == ';'); while (dim--) { *nc++ = '['; *nc++ = ']'; } assert(*nc == '\0'); classNameId = hprofLookupStringId(newName); free(newName); } else { classNameId = hprofLookupStringId(dotName); } free(dotName); return classNameId; } hprof_class_object_id hprofLookupClassId(const ClassObject *clazz) { void *val; if (clazz == NULL) { /* Someone's probably looking up the superclass * of java.lang.Object or of a primitive class. */ return (hprof_class_object_id)0; } dvmHashTableLock(gClassHashTable); /* We're using the hash table as a list. * TODO: replace the hash table with a more suitable structure */ val = dvmHashTableLookup(gClassHashTable, computeClassHash(clazz), (void *)clazz, classCmp, true); assert(val != NULL); dvmHashTableUnlock(gClassHashTable); /* Make sure that the class's name is in the string table. * This is a bunch of extra work that we only have to do * because of the order of tables in the output file * (strings need to be dumped before classes). */ getPrettyClassNameId(clazz->descriptor); return (hprof_class_object_id)clazz; } int hprofDumpClasses(hprof_context_t *ctx) { HashIter iter; hprof_record_t *rec = &ctx->curRec; int err; dvmHashTableLock(gClassHashTable); for (err = 0, dvmHashIterBegin(gClassHashTable, &iter); err == 0 && !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { err = hprofStartNewRecord(ctx, HPROF_TAG_LOAD_CLASS, HPROF_TIME); if (err == 0) { const ClassObject *clazz; clazz = (const ClassObject *)dvmHashIterData(&iter); assert(clazz != NULL); /* LOAD CLASS format: * * u4: class serial number (always > 0) * ID: class object ID * u4: stack trace serial number * ID: class name string ID * * We use the address of the class object structure as its ID. */ hprofAddU4ToRecord(rec, clazz->serialNumber); hprofAddIdToRecord(rec, (hprof_class_object_id)clazz); hprofAddU4ToRecord(rec, HPROF_NULL_STACK_TRACE); hprofAddIdToRecord(rec, getPrettyClassNameId(clazz->descriptor)); } } dvmHashTableUnlock(gClassHashTable); return err; }