| /* |
| * 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; |
| } |