/* * Send a notification when a thread's name changes. */ void dvmDdmSendThreadNameChange(int threadId, StringObject* newName) { if (!gDvm.ddmThreadNotification) return; size_t stringLen = dvmStringLen(newName); const u2* chars = dvmStringChars(newName); /* * Output format: * (4b) thread ID * (4b) stringLen * (xb) string chars */ int bufLen = 4 + 4 + (stringLen * 2); u1 buf[bufLen]; set4BE(&buf[0x00], threadId); set4BE(&buf[0x04], stringLen); u2* outChars = (u2*) &buf[0x08]; while (stringLen--) set2BE((u1*) (outChars++), *chars++); dvmDbgDdmSendChunk(CHUNK_TYPE("THNM"), bufLen, buf); }
/* * Generate the contents of a THST chunk. The data encompasses all known * threads. * * Response has: * (1b) header len * (1b) bytes per entry * (2b) thread count * Then, for each thread: * (4b) threadId * (1b) thread status * (4b) tid * (4b) utime * (4b) stime * (1b) is daemon? * * The length fields exist in anticipation of adding additional fields * without wanting to break ddms or bump the full protocol version. I don't * think it warrants full versioning. They might be extraneous and could * be removed from a future version. * * Returns a new byte[] with the data inside, or NULL on failure. The * caller must call dvmReleaseTrackedAlloc() on the array. */ ArrayObject* dvmDdmGenerateThreadStats(void) { const int kHeaderLen = 4; const int kBytesPerEntry = 18; dvmLockThreadList(NULL); Thread* thread; int threadCount = 0; for (thread = gDvm.threadList; thread != NULL; thread = thread->next) threadCount++; /* * Create a temporary buffer. We can't perform heap allocation with * the thread list lock held (could cause a GC). The output is small * enough to sit on the stack. */ int bufLen = kHeaderLen + threadCount * kBytesPerEntry; u1 tmpBuf[bufLen]; u1* buf = tmpBuf; set1(buf+0, kHeaderLen); set1(buf+1, kBytesPerEntry); set2BE(buf+2, (u2) threadCount); buf += kHeaderLen; pid_t pid = getpid(); for (thread = gDvm.threadList; thread != NULL; thread = thread->next) { unsigned long utime, stime; bool isDaemon; if (!getThreadStats(pid, thread->systemTid, &utime, &stime)) { // failed; drop in empty values utime = stime = 0; } isDaemon = dvmGetFieldBoolean(thread->threadObj, gDvm.offJavaLangThread_daemon); set4BE(buf+0, thread->threadId); set1(buf+4, thread->status); set4BE(buf+5, thread->systemTid); set4BE(buf+9, utime); set4BE(buf+13, stime); set1(buf+17, isDaemon); buf += kBytesPerEntry; } dvmUnlockThreadList(); /* * Create a byte array to hold the data. */ ArrayObject* arrayObj = dvmAllocPrimitiveArray('B', bufLen, ALLOC_DEFAULT); if (arrayObj != NULL) memcpy(arrayObj->contents, tmpBuf, bufLen); return arrayObj; }
/* * Send a notification when a thread starts or stops. * * Because we broadcast the full set of threads when the notifications are * first enabled, it's possible for "thread" to be actively executing. */ void dvmDdmSendThreadNotification(Thread* thread, bool started) { if (!gDvm.ddmThreadNotification) return; StringObject* nameObj = NULL; Object* threadObj = thread->threadObj; if (threadObj != NULL) { nameObj = (StringObject*) dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_name); } int type, len; u1 buf[256]; if (started) { const u2* chars; u2* outChars; size_t stringLen; type = CHUNK_TYPE("THCR"); if (nameObj != NULL) { stringLen = dvmStringLen(nameObj); chars = dvmStringChars(nameObj); } else { stringLen = 0; chars = NULL; } /* leave room for the two integer fields */ if (stringLen > (sizeof(buf) - sizeof(u4)*2) / 2) stringLen = (sizeof(buf) - sizeof(u4)*2) / 2; len = stringLen*2 + sizeof(u4)*2; set4BE(&buf[0x00], thread->threadId); set4BE(&buf[0x04], stringLen); /* copy the UTF-16 string, transforming to big-endian */ outChars = (u2*) &buf[0x08]; while (stringLen--) set2BE((u1*) (outChars++), *chars++); } else { type = CHUNK_TYPE("THDE"); len = 4; set4BE(&buf[0x00], thread->threadId); } dvmDbgDdmSendChunk(type, len, buf); }
/* * Append two big-endian bytes. */ void expandBufAdd2BE(ExpandBuf *pBuf, u2 val) { ensureSpace(pBuf, sizeof(val)); set2BE(pBuf->storage + pBuf->curLen, val); pBuf->curLen += sizeof(val); }