*/
#include "Dalvik.h"
+#ifdef HAVE_ANDROID_OS
+#include "cutils/properties.h"
+static bool isPowerOfTwo(int x) { return (x & (x - 1)) == 0; }
+#endif
+
#define kMaxAllocRecordStackDepth 16 /* max 255 */
-#define kNumAllocRecords 512 /* MUST be power of 2 */
+
+#define kDefaultNumAllocRecords 64*1024 /* MUST be power of 2 */
/*
* Record the details of an allocation.
const Method* method; /* which method we're executing in */
int pc; /* current execution offset, in 16-bit units */
} stackElem[kMaxAllocRecordStackDepth];
-
- /*
- * This was going to be either wall-clock time in seconds or monotonic
- * time in milliseconds since the VM started, to give a rough sense for
- * how long ago an allocation happened. This adds a system call per
- * allocation, which is too much overhead.
- */
- //u4 timestamp;
};
/*
* ===========================================================================
*/
+static int getAllocRecordMax() {
+#ifdef HAVE_ANDROID_OS
+ // Check whether there's a system property overriding the number of records.
+ const char* propertyName = "dalvik.vm.allocTrackerMax";
+ char allocRecordMaxString[PROPERTY_VALUE_MAX];
+ if (property_get(propertyName, allocRecordMaxString, "") > 0) {
+ char* end;
+ size_t value = strtoul(allocRecordMaxString, &end, 10);
+ if (*end != '\0') {
+ ALOGE("Ignoring %s '%s' --- invalid", propertyName, allocRecordMaxString);
+ return kDefaultNumAllocRecords;
+ }
+ if (!isPowerOfTwo(value)) {
+ ALOGE("Ignoring %s '%s' --- not power of two", propertyName, allocRecordMaxString);
+ return kDefaultNumAllocRecords;
+ }
+ return value;
+ }
+#endif
+ return kDefaultNumAllocRecords;
+}
+
/*
* Enable allocation tracking. Does nothing if tracking is already enabled.
*
dvmLockMutex(&gDvm.allocTrackerLock);
if (gDvm.allocRecords == NULL) {
- LOGI("Enabling alloc tracker (%d entries, %d frames --> %d bytes)",
- kNumAllocRecords, kMaxAllocRecordStackDepth,
- sizeof(AllocRecord) * kNumAllocRecords);
+ gDvm.allocRecordMax = getAllocRecordMax();
+
+ ALOGI("Enabling alloc tracker (%d entries, %d frames --> %d bytes)",
+ gDvm.allocRecordMax, kMaxAllocRecordStackDepth,
+ sizeof(AllocRecord) * gDvm.allocRecordMax);
gDvm.allocRecordHead = gDvm.allocRecordCount = 0;
- gDvm.allocRecords =
- (AllocRecord*) malloc(sizeof(AllocRecord) * kNumAllocRecords);
+ gDvm.allocRecords = (AllocRecord*) malloc(sizeof(AllocRecord) * gDvm.allocRecordMax);
if (gDvm.allocRecords == NULL)
result = false;
{
Thread* self = dvmThreadSelf();
if (self == NULL) {
- LOGW("alloc tracker: no thread");
+ ALOGW("alloc tracker: no thread");
return;
}
}
/* advance and clip */
- if (++gDvm.allocRecordHead == kNumAllocRecords)
+ if (++gDvm.allocRecordHead == gDvm.allocRecordMax)
gDvm.allocRecordHead = 0;
AllocRecord* pRec = &gDvm.allocRecords[gDvm.allocRecordHead];
pRec->threadId = self->threadId;
getStackFrames(self, pRec);
- if (gDvm.allocRecordCount < kNumAllocRecords)
+ if (gDvm.allocRecordCount < gDvm.allocRecordMax)
gDvm.allocRecordCount++;
dvmUnlockMutex(&gDvm.allocTrackerLock);
followed by UTF-16 data.
We send up 16-bit unsigned indexes into string tables. In theory there
-can be (kMaxAllocRecordStackDepth * kNumAllocRecords) unique strings in
+can be (kMaxAllocRecordStackDepth * gDvm.allocRecordMax) unique strings in
each table, but in practice there should be far fewer.
The chief reason for using a string table here is to keep the size of
* from it.
*
* We need to handle underflow in our circular buffer, so we add
- * kNumAllocRecords and then mask it back down.
+ * gDvm.allocRecordMax and then mask it back down.
*/
inline static int headIndex()
{
- return (gDvm.allocRecordHead+1 + kNumAllocRecords - gDvm.allocRecordCount)
- & (kNumAllocRecords-1);
+ return (gDvm.allocRecordHead+1 + gDvm.allocRecordMax - gDvm.allocRecordCount)
+ & (gDvm.allocRecordMax-1);
}
/*
fileCount++;
}
- idx = (idx + 1) & (kNumAllocRecords-1);
+ idx = (idx + 1) & (gDvm.allocRecordMax-1);
}
- LOGI("class %d/%d, method %d/%d, file %d/%d",
+ ALOGI("class %d/%d, method %d/%d, file %d/%d",
dvmPointerSetGetCount(classNames), classCount,
dvmPointerSetGetCount(methodNames), methodCount,
dvmPointerSetGetCount(fileNames), fileCount);
ptr += kStackFrameLen;
}
- idx = (idx + 1) & (kNumAllocRecords-1);
+ idx = (idx + 1) & (gDvm.allocRecordMax-1);
}
return ptr - origPtr;
methodNames = dvmPointerSetAlloc(128);
fileNames = dvmPointerSetAlloc(128);
if (classNames == NULL || methodNames == NULL || fileNames == NULL) {
- LOGE("Failed allocating pointer sets");
+ ALOGE("Failed allocating pointer sets");
goto bail;
}
totalSize += computeStringTableSize(classNames);
totalSize += computeStringTableSize(methodNames);
totalSize += computeStringTableSize(fileNames);
- LOGI("Generated AT, size is %zd/%zd", baseSize, totalSize);
+ ALOGI("Generated AT, size is %zd/%zd", baseSize, totalSize);
/*
* Part 3: allocate a buffer and generate the output.
strPtr += outputStringTable(methodNames, strPtr);
strPtr += outputStringTable(fileNames, strPtr);
if (strPtr - buffer != (int)totalSize) {
- LOGE("size mismatch (%d vs %zd)", strPtr - buffer, totalSize);
+ ALOGE("size mismatch (%d vs %zd)", strPtr - buffer, totalSize);
dvmAbort();
}
//dvmPrintHexDump(buffer, totalSize);
int idx = headIndex();
int count = gDvm.allocRecordCount;
- LOGI("Tracked allocations, (head=%d count=%d)",
+ ALOGI("Tracked allocations, (head=%d count=%d)",
gDvm.allocRecordHead, count);
while (count--) {
AllocRecord* pRec = &gDvm.allocRecords[idx];
- LOGI(" T=%-2d %6d %s",
+ ALOGI(" T=%-2d %6d %s",
pRec->threadId, pRec->size, pRec->clazz->descriptor);
if (true) {
const Method* method = pRec->stackElem[i].method;
if (dvmIsNativeMethod(method)) {
- LOGI(" %s.%s (Native)",
+ ALOGI(" %s.%s (Native)",
method->clazz->descriptor, method->name);
} else {
- LOGI(" %s.%s +%d",
+ ALOGI(" %s.%s +%d",
method->clazz->descriptor, method->name,
pRec->stackElem[i].pc);
}
if ((count % 5) == 0)
usleep(40000);
- idx = (idx + 1) & (kNumAllocRecords-1);
+ idx = (idx + 1) & (gDvm.allocRecordMax-1);
}
dvmUnlockMutex(&gDvm.allocTrackerLock);