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