blob: d441ec4625945b67a1df4896c120c6d8d5e11ef4 [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Andy McFadden50cab512010-10-07 15:11:43 -070016
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080017/*
18 * Handle Dalvik Debug Monitor requests and events.
19 *
20 * Remember that all DDM traffic is big-endian since it travels over the
21 * JDWP connection.
22 */
23#include "Dalvik.h"
24
25#include <fcntl.h>
26#include <errno.h>
27
28/*
29 * "buf" contains a full JDWP packet, possibly with multiple chunks. We
30 * need to process each, accumulate the replies, and ship the whole thing
31 * back.
32 *
33 * Returns "true" if we have a reply. The reply buffer is newly allocated,
34 * and includes the chunk type/length, followed by the data.
35 *
36 * TODO: we currently assume that the request and reply include a single
37 * chunk. If this becomes inconvenient we will need to adapt.
38 */
39bool dvmDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
40 int* pReplyLen)
41{
42 Thread* self = dvmThreadSelf();
43 const int kChunkHdrLen = 8;
44 ArrayObject* dataArray = NULL;
Andy McFadden50cab512010-10-07 15:11:43 -070045 Object* chunk = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080046 bool result = false;
47
48 assert(dataLen >= 0);
49
Andy McFadden4b17a1d2011-03-29 09:58:27 -070050 if (!dvmIsClassInitialized(gDvm.classOrgApacheHarmonyDalvikDdmcChunk)) {
51 if (!dvmInitClass(gDvm.classOrgApacheHarmonyDalvikDdmcChunk)) {
52 dvmLogExceptionStackTrace();
53 dvmClearException(self);
54 goto bail;
55 }
56 }
57
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080058 /*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080059 * The chunk handlers are written in the Java programming language, so
60 * we need to convert the buffer to a byte array.
61 */
62 dataArray = dvmAllocPrimitiveArray('B', dataLen, ALLOC_DEFAULT);
63 if (dataArray == NULL) {
Steve Blocke8e1ddc2012-01-05 23:21:27 +000064 ALOGW("array alloc failed (%d)", dataLen);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080065 dvmClearException(self);
66 goto bail;
67 }
68 memcpy(dataArray->contents, buf, dataLen);
69
70 /*
71 * Run through and find all chunks. [Currently just find the first.]
72 */
73 unsigned int offset, length, type;
74 type = get4BE((u1*)dataArray->contents + 0);
75 length = get4BE((u1*)dataArray->contents + 4);
76 offset = kChunkHdrLen;
77 if (offset+length > (unsigned int) dataLen) {
Steve Blocke8e1ddc2012-01-05 23:21:27 +000078 ALOGW("WARNING: bad chunk found (len=%u pktLen=%d)", length, dataLen);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080079 goto bail;
80 }
81
82 /*
83 * Call the handler.
84 */
85 JValue callRes;
Andy McFaddence1762c2011-03-28 15:03:21 -070086 dvmCallMethod(self, gDvm.methDalvikDdmcServer_dispatch, NULL, &callRes,
87 type, dataArray, offset, length);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080088 if (dvmCheckException(self)) {
Steve Block43084172012-01-04 20:04:51 +000089 ALOGI("Exception thrown by dispatcher for 0x%08x", type);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080090 dvmLogExceptionStackTrace();
91 dvmClearException(self);
92 goto bail;
93 }
94
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080095 ArrayObject* replyData;
96 chunk = (Object*) callRes.l;
97 if (chunk == NULL)
98 goto bail;
99
Andy McFadden50cab512010-10-07 15:11:43 -0700100 /* not strictly necessary -- we don't alloc from managed heap here */
101 dvmAddTrackedAlloc(chunk, self);
102
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800103 /*
104 * Pull the pieces out of the chunk. We copy the results into a
105 * newly-allocated buffer that the caller can free. We don't want to
106 * continue using the Chunk object because nothing has a reference to it.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800107 *
108 * We could avoid this by returning type/data/offset/length and having
109 * the caller be aware of the object lifetime issues, but that
110 * integrates the JDWP code more tightly into the VM, and doesn't work
111 * if we have responses for multiple chunks.
112 *
113 * So we're pretty much stuck with copying data around multiple times.
114 */
Andy McFaddence1762c2011-03-28 15:03:21 -0700115 type = dvmGetFieldInt(chunk, gDvm.offDalvikDdmcChunk_type);
116 replyData =
117 (ArrayObject*) dvmGetFieldObject(chunk, gDvm.offDalvikDdmcChunk_data);
118 offset = dvmGetFieldInt(chunk, gDvm.offDalvikDdmcChunk_offset);
119 length = dvmGetFieldInt(chunk, gDvm.offDalvikDdmcChunk_length);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800120
Steve Block92c1f6f2011-10-20 11:55:54 +0100121 ALOGV("DDM reply: type=0x%08x data=%p offset=%d length=%d",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800122 type, replyData, offset, length);
123
124 if (length == 0 || replyData == NULL)
125 goto bail;
126 if (offset + length > replyData->length) {
Steve Blocke8e1ddc2012-01-05 23:21:27 +0000127 ALOGW("WARNING: chunk off=%d len=%d exceeds reply array len %d",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800128 offset, length, replyData->length);
129 goto bail;
130 }
131
132 u1* reply;
133 reply = (u1*) malloc(length + kChunkHdrLen);
134 if (reply == NULL) {
Steve Blocke8e1ddc2012-01-05 23:21:27 +0000135 ALOGW("malloc %d failed", length+kChunkHdrLen);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800136 goto bail;
137 }
138 set4BE(reply + 0, type);
139 set4BE(reply + 4, length);
140 memcpy(reply+kChunkHdrLen, (const u1*)replyData->contents + offset, length);
141
142 *pReplyBuf = reply;
143 *pReplyLen = length + kChunkHdrLen;
144 result = true;
145
Steve Block92c1f6f2011-10-20 11:55:54 +0100146 ALOGV("dvmHandleDdm returning type=%.4s buf=%p len=%d",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800147 (char*) reply, reply, length);
148
149bail:
Andy McFadden50cab512010-10-07 15:11:43 -0700150 dvmReleaseTrackedAlloc((Object*) dataArray, self);
151 dvmReleaseTrackedAlloc(chunk, self);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800152 return result;
153}
154
155/* defined in org.apache.harmony.dalvik.ddmc.DdmServer */
156#define CONNECTED 1
157#define DISCONNECTED 2
158
159/*
160 * Broadcast an event to all handlers.
161 */
162static void broadcast(int event)
163{
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800164 Thread* self = dvmThreadSelf();
165
166 if (self->status != THREAD_RUNNING) {
Steve Blockc1a4ab92012-01-06 19:16:58 +0000167 ALOGE("ERROR: DDM broadcast with thread status=%d", self->status);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800168 /* try anyway? */
169 }
170
Andy McFadden4b17a1d2011-03-29 09:58:27 -0700171 if (!dvmIsClassInitialized(gDvm.classOrgApacheHarmonyDalvikDdmcDdmServer)) {
172 if (!dvmInitClass(gDvm.classOrgApacheHarmonyDalvikDdmcDdmServer)) {
173 dvmLogExceptionStackTrace();
174 dvmClearException(self);
175 return;
176 }
177 }
178
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800179 JValue unused;
Andy McFaddence1762c2011-03-28 15:03:21 -0700180 dvmCallMethod(self, gDvm.methDalvikDdmcServer_broadcast, NULL, &unused,
181 event);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800182 if (dvmCheckException(self)) {
Steve Block43084172012-01-04 20:04:51 +0000183 ALOGI("Exception thrown by broadcast(%d)", event);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800184 dvmLogExceptionStackTrace();
185 dvmClearException(self);
Andy McFadden4b17a1d2011-03-29 09:58:27 -0700186 return;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800187 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800188}
189
190/*
191 * First DDM packet has arrived over JDWP. Notify the press.
192 *
193 * We can do some initialization here too.
194 */
Carl Shapiro1e1433e2011-04-20 16:51:38 -0700195void dvmDdmConnected()
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800196{
197 // TODO: any init
198
Steve Block92c1f6f2011-10-20 11:55:54 +0100199 ALOGV("Broadcasting DDM connect");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800200 broadcast(CONNECTED);
201}
202
203/*
204 * JDWP connection has dropped.
205 *
206 * Do some cleanup.
207 */
Carl Shapiro1e1433e2011-04-20 16:51:38 -0700208void dvmDdmDisconnected()
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800209{
Steve Block92c1f6f2011-10-20 11:55:54 +0100210 ALOGV("Broadcasting DDM disconnect");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800211 broadcast(DISCONNECTED);
212
213 gDvm.ddmThreadNotification = false;
214}
215
216
217/*
218 * Turn thread notification on or off.
219 */
220void dvmDdmSetThreadNotification(bool enable)
221{
222 /*
223 * We lock the thread list to avoid sending duplicate events or missing
224 * a thread change. We should be okay holding this lock while sending
225 * the messages out. (We have to hold it while accessing a live thread.)
226 */
227 dvmLockThreadList(NULL);
228 gDvm.ddmThreadNotification = enable;
229
230 if (enable) {
231 Thread* thread;
232 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
Steve Blocke8e1ddc2012-01-05 23:21:27 +0000233 //ALOGW("notify %d", thread->threadId);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800234 dvmDdmSendThreadNotification(thread, true);
235 }
236 }
237
238 dvmUnlockThreadList();
239}
240
241/*
242 * Send a notification when a thread starts or stops.
243 *
244 * Because we broadcast the full set of threads when the notifications are
245 * first enabled, it's possible for "thread" to be actively executing.
246 */
247void dvmDdmSendThreadNotification(Thread* thread, bool started)
248{
Elliott Hughesd8a3f9f2011-06-17 15:59:16 -0700249 if (!gDvm.ddmThreadNotification) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800250 return;
Elliott Hughesd8a3f9f2011-06-17 15:59:16 -0700251 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800252
Andy McFaddenbbc2acc2010-08-24 14:23:11 -0700253 StringObject* nameObj = NULL;
254 Object* threadObj = thread->threadObj;
255
256 if (threadObj != NULL) {
257 nameObj = (StringObject*)
258 dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_name);
259 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800260
261 int type, len;
262 u1 buf[256];
263
264 if (started) {
265 const u2* chars;
266 u2* outChars;
267 size_t stringLen;
268
269 type = CHUNK_TYPE("THCR");
270
271 if (nameObj != NULL) {
Elliott Hughesd8a3f9f2011-06-17 15:59:16 -0700272 stringLen = nameObj->length();
273 chars = nameObj->chars();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800274 } else {
275 stringLen = 0;
276 chars = NULL;
277 }
278
279 /* leave room for the two integer fields */
Elliott Hughesd8a3f9f2011-06-17 15:59:16 -0700280 if (stringLen > (sizeof(buf) - sizeof(u4)*2) / 2) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800281 stringLen = (sizeof(buf) - sizeof(u4)*2) / 2;
Elliott Hughesd8a3f9f2011-06-17 15:59:16 -0700282 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800283 len = stringLen*2 + sizeof(u4)*2;
284
285 set4BE(&buf[0x00], thread->threadId);
286 set4BE(&buf[0x04], stringLen);
287
288 /* copy the UTF-16 string, transforming to big-endian */
Carl Shapirod5c36b92011-04-15 18:38:06 -0700289 outChars = (u2*)(void*)&buf[0x08];
Elliott Hughesd8a3f9f2011-06-17 15:59:16 -0700290 while (stringLen--) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800291 set2BE((u1*) (outChars++), *chars++);
Elliott Hughesd8a3f9f2011-06-17 15:59:16 -0700292 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800293 } else {
294 type = CHUNK_TYPE("THDE");
295
296 len = 4;
297
298 set4BE(&buf[0x00], thread->threadId);
299 }
300
301 dvmDbgDdmSendChunk(type, len, buf);
302}
303
304/*
305 * Send a notification when a thread's name changes.
306 */
307void dvmDdmSendThreadNameChange(int threadId, StringObject* newName)
308{
Elliott Hughesd8a3f9f2011-06-17 15:59:16 -0700309 if (!gDvm.ddmThreadNotification) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800310 return;
Elliott Hughesd8a3f9f2011-06-17 15:59:16 -0700311 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800312
Elliott Hughesd8a3f9f2011-06-17 15:59:16 -0700313 size_t stringLen = newName->length();
314 const u2* chars = newName->chars();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800315
316 /*
317 * Output format:
318 * (4b) thread ID
319 * (4b) stringLen
320 * (xb) string chars
321 */
322 int bufLen = 4 + 4 + (stringLen * 2);
323 u1 buf[bufLen];
324
325 set4BE(&buf[0x00], threadId);
326 set4BE(&buf[0x04], stringLen);
Carl Shapirod5c36b92011-04-15 18:38:06 -0700327 u2* outChars = (u2*)(void*)&buf[0x08];
Elliott Hughesd8a3f9f2011-06-17 15:59:16 -0700328 while (stringLen--) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800329 set2BE((u1*) (outChars++), *chars++);
Elliott Hughesd8a3f9f2011-06-17 15:59:16 -0700330 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800331
332 dvmDbgDdmSendChunk(CHUNK_TYPE("THNM"), bufLen, buf);
333}
334
335/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800336 * Generate the contents of a THST chunk. The data encompasses all known
337 * threads.
338 *
339 * Response has:
340 * (1b) header len
341 * (1b) bytes per entry
342 * (2b) thread count
343 * Then, for each thread:
344 * (4b) threadId
345 * (1b) thread status
346 * (4b) tid
Carl Shapirode750892010-06-08 16:37:12 -0700347 * (4b) utime
348 * (4b) stime
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800349 * (1b) is daemon?
350 *
351 * The length fields exist in anticipation of adding additional fields
352 * without wanting to break ddms or bump the full protocol version. I don't
353 * think it warrants full versioning. They might be extraneous and could
354 * be removed from a future version.
355 *
356 * Returns a new byte[] with the data inside, or NULL on failure. The
357 * caller must call dvmReleaseTrackedAlloc() on the array.
358 */
Carl Shapiro1e1433e2011-04-20 16:51:38 -0700359ArrayObject* dvmDdmGenerateThreadStats()
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800360{
361 const int kHeaderLen = 4;
362 const int kBytesPerEntry = 18;
363
364 dvmLockThreadList(NULL);
365
366 Thread* thread;
367 int threadCount = 0;
368 for (thread = gDvm.threadList; thread != NULL; thread = thread->next)
369 threadCount++;
370
371 /*
372 * Create a temporary buffer. We can't perform heap allocation with
373 * the thread list lock held (could cause a GC). The output is small
374 * enough to sit on the stack.
375 */
376 int bufLen = kHeaderLen + threadCount * kBytesPerEntry;
377 u1 tmpBuf[bufLen];
378 u1* buf = tmpBuf;
379
380 set1(buf+0, kHeaderLen);
381 set1(buf+1, kBytesPerEntry);
382 set2BE(buf+2, (u2) threadCount);
383 buf += kHeaderLen;
384
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800385 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
Andy McFaddenbbc2acc2010-08-24 14:23:11 -0700386 bool isDaemon = false;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800387
Andy McFadden0a3f6982010-08-31 13:50:08 -0700388 ProcStatData procStatData;
389 if (!dvmGetThreadStats(&procStatData, thread->systemTid)) {
390 /* failed; show zero */
391 memset(&procStatData, 0, sizeof(procStatData));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800392 }
393
Andy McFaddenbbc2acc2010-08-24 14:23:11 -0700394 Object* threadObj = thread->threadObj;
395 if (threadObj != NULL) {
396 isDaemon = dvmGetFieldBoolean(threadObj,
397 gDvm.offJavaLangThread_daemon);
398 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800399
400 set4BE(buf+0, thread->threadId);
401 set1(buf+4, thread->status);
402 set4BE(buf+5, thread->systemTid);
Andy McFadden0a3f6982010-08-31 13:50:08 -0700403 set4BE(buf+9, procStatData.utime);
404 set4BE(buf+13, procStatData.stime);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800405 set1(buf+17, isDaemon);
406
407 buf += kBytesPerEntry;
408 }
409 dvmUnlockThreadList();
410
411
412 /*
413 * Create a byte array to hold the data.
414 */
415 ArrayObject* arrayObj = dvmAllocPrimitiveArray('B', bufLen, ALLOC_DEFAULT);
416 if (arrayObj != NULL)
417 memcpy(arrayObj->contents, tmpBuf, bufLen);
418 return arrayObj;
419}
420
421
422/*
423 * Find the specified thread and return its stack trace as an array of
424 * StackTraceElement objects.
425 */
426ArrayObject* dvmDdmGetStackTraceById(u4 threadId)
427{
428 Thread* self = dvmThreadSelf();
429 Thread* thread;
430 int* traceBuf;
431
432 dvmLockThreadList(self);
433
434 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
435 if (thread->threadId == threadId)
436 break;
437 }
438 if (thread == NULL) {
Steve Block43084172012-01-04 20:04:51 +0000439 ALOGI("dvmDdmGetStackTraceById: threadid=%d not found", threadId);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800440 dvmUnlockThreadList();
441 return NULL;
442 }
443
444 /*
445 * Suspend the thread, pull out the stack trace, then resume the thread
446 * and release the thread list lock. If we're being asked to examine
447 * our own stack trace, skip the suspend/resume.
448 */
Brian Carlstrom6b1f9f82011-03-28 00:12:36 -0700449 size_t stackDepth;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800450 if (thread != self)
451 dvmSuspendThread(thread);
452 traceBuf = dvmFillInStackTraceRaw(thread, &stackDepth);
453 if (thread != self)
454 dvmResumeThread(thread);
455 dvmUnlockThreadList();
456
457 /*
458 * Convert the raw buffer into an array of StackTraceElement.
459 */
460 ArrayObject* trace = dvmGetStackTraceRaw(traceBuf, stackDepth);
461 free(traceBuf);
462 return trace;
463}
464
465/*
466 * Gather up the allocation data and copy it into a byte[].
467 *
468 * Returns NULL on failure with an exception raised.
469 */
Carl Shapiro1e1433e2011-04-20 16:51:38 -0700470ArrayObject* dvmDdmGetRecentAllocations()
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800471{
472 u1* data;
473 size_t len;
474
475 if (!dvmGenerateTrackedAllocationReport(&data, &len)) {
476 /* assume OOM */
Dan Bornsteind27f3cf2011-02-23 13:07:07 -0800477 dvmThrowOutOfMemoryError("recent alloc native");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800478 return NULL;
479 }
480
481 ArrayObject* arrayObj = dvmAllocPrimitiveArray('B', len, ALLOC_DEFAULT);
482 if (arrayObj != NULL)
483 memcpy(arrayObj->contents, data, len);
484 return arrayObj;
485}