/* * This is a qsort() callback. We sort Object* by class, allocation size, * and then by the Object* itself. */ static int compareObject(const void* vobj1, const void* vobj2) { Object* obj1 = *((Object**) vobj1); Object* obj2 = *((Object**) vobj2); if (obj1 == NULL || obj2 == NULL) return (u1*)obj1 - (u1*)obj2; if (obj1->clazz != obj2->clazz) { return (u1*)obj1->clazz - (u1*)obj2->clazz; } else { int size1 = dvmObjectSizeInHeap(obj1); int size2 = dvmObjectSizeInHeap(obj2); if (size1 != size2) { return size1 - size2; } else { return (u1*)obj1 - (u1*)obj2; } } }
/* * Create a copy of an object, for Object.clone(). * * We use the size actually allocated, rather than obj->clazz->objectSize, * because the latter doesn't work for array objects. */ Object* dvmCloneObject(Object* obj) { Object* copy; int size; int flags; assert(dvmIsValidObject(obj)); /* Class.java shouldn't let us get here (java.lang.Class is final * and does not implement Clonable), but make extra sure. * A memcpy() clone will wreak havoc on a ClassObject's "innards". */ assert(obj->clazz != gDvm.classJavaLangClass); if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISFINALIZABLE)) flags = ALLOC_DEFAULT | ALLOC_FINALIZABLE; else flags = ALLOC_DEFAULT; //TODO: use clazz->objectSize for non-arrays size = dvmObjectSizeInHeap(obj); copy = dvmMalloc(size, flags); if (copy == NULL) return NULL; #if WITH_HPROF && WITH_HPROF_STACK hprofFillInStackTrace(copy); dvmTrackAllocation(obj->clazz, size); #endif memcpy(copy, obj, size); DVM_LOCK_INIT(©->lock); Monitor* mon = NULL;//dvmCreateMonitor(copy); copy->lock = (u4)mon | LW_SHAPE_FAT; //LOGV("CloneObject: %p->%p %s (%d)\n", obj, copy, obj->clazz->name, size); // TODO: deal with reference classes /* don't call dvmReleaseTrackedAlloc -- the caller must do that */ return copy; }
/* * Dump a summary of an array of references to the log file. * * This is used to dump the contents of ReferenceTable and IndirectRefTable * structs. */ void dvmDumpReferenceTableContents(Object* const* refs, size_t count, const char* descr) { LOGW("%s reference table (%p) dump:", descr, refs); if (count == 0) { LOGW(" (empty)"); return; } // Dump the most recent N entries. const size_t kLast = 10; int first = count - kLast; if (first < 0) { first = 0; } LOGW(" Last %d entries (of %d):", (count - first), count); for (int idx = count - 1; idx >= first; --idx) { const Object* ref = refs[idx]; if (ref == NULL) { continue; } if (ref == kClearedJniWeakGlobal) { LOGW(" %5d: cleared jweak", idx); continue; } if (ref->clazz == NULL) { // should only be possible right after a plain dvmMalloc(). size_t size = dvmObjectSizeInHeap(ref); LOGW(" %5d: %p (raw) (%zd bytes)", idx, ref, size); continue; } std::string className(dvmHumanReadableType(ref)); std::string extras; size_t elems = getElementCount(ref); if (elems != 0) { StringAppendF(&extras, " (%zd elements)", elems); } else if (ref->clazz == gDvm.classJavaLangString) { const StringObject* str = reinterpret_cast<const StringObject*>(ref); extras += " \""; size_t count = 0; char* s = dvmCreateCstrFromString(str); char* p = s; for (; *p && count < 16; ++p, ++count) { extras += *p; } if (*p == 0) { extras += "\""; } else { StringAppendF(&extras, "... (%d chars)", str->length()); } free(s); } LOGW(" %5d: %p %s%s", idx, ref, className.c_str(), extras.c_str()); } // Make a copy of the table, and sort it. Object** tableCopy = (Object**)malloc(sizeof(Object*) * count); if (tableCopy == NULL) { LOGE("Unable to copy table with %d elements", count); return; } memcpy(tableCopy, refs, sizeof(Object*) * count); qsort(tableCopy, count, sizeof(Object*), compareObject); refs = tableCopy; // use sorted list // Remove any uninteresting stuff from the list. The sort moved them all to the end. while (count > 0 && refs[count-1] == NULL) { --count; } while (count > 0 && refs[count-1] == kClearedJniWeakGlobal) { --count; } if (count == 0) { return; } // Dump a summary of the whole table. LOGW(" Summary:"); size_t equiv, identical; equiv = identical = 0; size_t idx; size_t elems; for (idx = 1; idx < count; idx++) { elems = getElementCount(refs[idx-1]); if (refs[idx] == refs[idx-1]) { // same reference, added more than once. identical++; } else if (refs[idx]->clazz == refs[idx-1]->clazz && getElementCount(refs[idx]) == elems) { // same class / element count, different object. equiv++; } else { // different class. logSummaryLine(refs[idx-1], elems, identical, equiv); equiv = identical = 0; } } // Handle the last entry (everything above outputs refs[i-1]). elems = getElementCount(refs[idx-1]); logSummaryLine(refs[count-1], elems, identical, equiv); free(tableCopy); }
/* * Dump the contents of a ReferenceTable to the log. * * The caller should lock any external sync before calling. * * (This was originally written to be tolerant of null entries in the table. * I don't think that can happen anymore.) */ void dvmDumpReferenceTable(const ReferenceTable* pRef, const char* descr) { const int kLast = 10; int count = dvmReferenceTableEntries(pRef); Object** refs; int i; if (count == 0) { LOGW("%s reference table has no entries\n", descr); return; } assert(count > 0); /* * Dump the most recent N entries. */ LOGW("Last %d entries in %s reference table:\n", kLast, descr); refs = pRef->table; // use unsorted list int size; int start = count - kLast; if (start < 0) start = 0; for (i = start; i < count; i++) { size = (refs[i] == NULL) ? 0 : dvmObjectSizeInHeap(refs[i]); Object* ref = refs[i]; if (ref->clazz == gDvm.classJavaLangClass) { ClassObject* clazz = (ClassObject*) ref; LOGW("%5d: %p cls=%s '%s' (%d bytes)\n", i, ref, (refs[i] == NULL) ? "-" : ref->clazz->descriptor, clazz->descriptor, size); } else if (ref->clazz == NULL) { /* should only be possible right after a plain dvmMalloc() */ LOGW("%5d: %p cls=(raw) (%d bytes)\n", i, ref, size); } else { LOGW("%5d: %p cls=%s (%d bytes)\n", i, ref, (refs[i] == NULL) ? "-" : ref->clazz->descriptor, size); } } /* * Make a copy of the table, and sort it. */ Object** tableCopy = (Object**)malloc(sizeof(Object*) * count); memcpy(tableCopy, pRef->table, sizeof(Object*) * count); qsort(tableCopy, count, sizeof(Object*), compareObject); refs = tableCopy; // use sorted list /* * Dump uniquified table summary. While we're at it, generate a * cumulative total amount of pinned memory based on the unique entries. */ LOGW("%s reference table summary (%d entries):\n", descr, count); int equiv, identical, total; total = equiv = identical = 0; for (i = 1; i < count; i++) { size = (refs[i-1] == NULL) ? 0 : dvmObjectSizeInHeap(refs[i-1]); if (refs[i] == refs[i-1]) { /* same reference, added more than once */ identical++; } else if (refs[i]->clazz == refs[i-1]->clazz && (int) dvmObjectSizeInHeap(refs[i]) == size) { /* same class / size, different object */ total += size; equiv++; } else { /* different class */ total += size; logObject(refs[i-1], size, identical, equiv); equiv = identical = 0; } } /* handle the last entry (everything above outputs refs[i-1]) */ size = (refs[count-1] == NULL) ? 0 : dvmObjectSizeInHeap(refs[count-1]); total += size; logObject(refs[count-1], size, identical, equiv); LOGW("Memory held directly by tracked refs is %d bytes\n", total); free(tableCopy); }