JdwpHandler.cpp revision bb046193c2da5d43a57b2fa8a17a0f634bf003ba
1/*
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 */
16
17/*
18 * Handle messages from debugger.
19 *
20 * GENERAL NOTE: we're not currently testing the message length for
21 * correctness.  This is usually a bad idea, but here we can probably
22 * get away with it so long as the debugger isn't broken.  We can
23 * change the "read" macros to use "dataLen" to avoid wandering into
24 * bad territory, and have a single "is dataLen correct" check at the
25 * end of each function.  Not needed at this time.
26 */
27#include "jdwp/JdwpPriv.h"
28#include "jdwp/JdwpHandler.h"
29#include "jdwp/JdwpEvent.h"
30#include "jdwp/JdwpConstants.h"
31#include "jdwp/ExpandBuf.h"
32
33#include "Bits.h"
34#include "Atomic.h"
35#include "DalvikVersion.h"
36
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40
41/*
42 * Helper function: read a "location" from an input buffer.
43 */
44static void jdwpReadLocation(const u1** pBuf, JdwpLocation* pLoc)
45{
46    memset(pLoc, 0, sizeof(*pLoc));     /* allows memcmp() later */
47    pLoc->typeTag = read1(pBuf);
48    pLoc->classId = dvmReadObjectId(pBuf);
49    pLoc->methodId = dvmReadMethodId(pBuf);
50    pLoc->idx = read8BE(pBuf);
51}
52
53/*
54 * Helper function: write a "location" into the reply buffer.
55 */
56void dvmJdwpAddLocation(ExpandBuf* pReply, const JdwpLocation* pLoc)
57{
58    expandBufAdd1(pReply, pLoc->typeTag);
59    expandBufAddObjectId(pReply, pLoc->classId);
60    expandBufAddMethodId(pReply, pLoc->methodId);
61    expandBufAdd8BE(pReply, pLoc->idx);
62}
63
64/*
65 * Helper function: read a variable-width value from the input buffer.
66 */
67static u8 jdwpReadValue(const u1** pBuf, int width)
68{
69    u8 value;
70
71    switch (width) {
72    case 1:     value = read1(pBuf);                break;
73    case 2:     value = read2BE(pBuf);              break;
74    case 4:     value = read4BE(pBuf);              break;
75    case 8:     value = read8BE(pBuf);              break;
76    default:    value = (u8) -1; assert(false);     break;
77    }
78
79    return value;
80}
81
82/*
83 * Helper function: write a variable-width value into the output input buffer.
84 */
85static void jdwpWriteValue(ExpandBuf* pReply, int width, u8 value)
86{
87    switch (width) {
88    case 1:     expandBufAdd1(pReply, value);       break;
89    case 2:     expandBufAdd2BE(pReply, value);     break;
90    case 4:     expandBufAdd4BE(pReply, value);     break;
91    case 8:     expandBufAdd8BE(pReply, value);     break;
92    default:    assert(false);                      break;
93    }
94}
95
96/*
97 * Common code for *_InvokeMethod requests.
98 *
99 * If "isConstructor" is set, this returns "objectId" rather than the
100 * expected-to-be-void return value of the called function.
101 */
102static JdwpError finishInvoke(JdwpState* state,
103    const u1* buf, int dataLen, ExpandBuf* pReply,
104    ObjectId threadId, ObjectId objectId, RefTypeId classId, MethodId methodId,
105    bool isConstructor)
106{
107    assert(!isConstructor || objectId != 0);
108
109    u4 numArgs = read4BE(&buf);
110
111    ALOGV("    --> threadId=%llx objectId=%llx", threadId, objectId);
112    ALOGV("        classId=%llx methodId=%x %s.%s",
113        classId, methodId,
114        dvmDbgGetClassDescriptor(classId),
115        dvmDbgGetMethodName(classId, methodId));
116    ALOGV("        %d args:", numArgs);
117
118    u8* argArray = NULL;
119    if (numArgs > 0)
120        argArray = (ObjectId*) malloc(sizeof(ObjectId) * numArgs);
121
122    for (u4 i = 0; i < numArgs; i++) {
123        u1 typeTag = read1(&buf);
124        int width = dvmDbgGetTagWidth(typeTag);
125        u8 value = jdwpReadValue(&buf, width);
126
127        ALOGV("          '%c'(%d): 0x%llx", typeTag, width, value);
128        argArray[i] = value;
129    }
130
131    u4 options = read4BE(&buf);  /* enum InvokeOptions bit flags */
132    ALOGV("        options=0x%04x%s%s", options,
133        (options & INVOKE_SINGLE_THREADED) ? " (SINGLE_THREADED)" : "",
134        (options & INVOKE_NONVIRTUAL) ? " (NONVIRTUAL)" : "");
135
136
137    u1 resultTag;
138    u8 resultValue;
139    ObjectId exceptObjId;
140    JdwpError err = dvmDbgInvokeMethod(threadId, objectId, classId, methodId,
141            numArgs, argArray, options,
142            &resultTag, &resultValue, &exceptObjId);
143    if (err != ERR_NONE)
144        goto bail;
145
146    if (err == ERR_NONE) {
147        if (isConstructor) {
148            expandBufAdd1(pReply, JT_OBJECT);
149            expandBufAddObjectId(pReply, objectId);
150        } else {
151            int width = dvmDbgGetTagWidth(resultTag);
152
153            expandBufAdd1(pReply, resultTag);
154            if (width != 0)
155                jdwpWriteValue(pReply, width, resultValue);
156        }
157        expandBufAdd1(pReply, JT_OBJECT);
158        expandBufAddObjectId(pReply, exceptObjId);
159
160        ALOGV("  --> returned '%c' 0x%llx (except=%08llx)",
161            resultTag, resultValue, exceptObjId);
162
163        /* show detailed debug output */
164        if (resultTag == JT_STRING && exceptObjId == 0) {
165            if (resultValue != 0) {
166                char* str = dvmDbgStringToUtf8(resultValue);
167                ALOGV("      string '%s'", str);
168                free(str);
169            } else {
170                ALOGV("      string (null)");
171            }
172        }
173    }
174
175bail:
176    free(argArray);
177    return err;
178}
179
180
181/*
182 * Request for version info.
183 */
184static JdwpError handleVM_Version(JdwpState* state, const u1* buf,
185    int dataLen, ExpandBuf* pReply)
186{
187    char tmpBuf[128];
188
189    /* text information on VM version */
190    sprintf(tmpBuf, "Android DalvikVM %d.%d.%d",
191        DALVIK_MAJOR_VERSION, DALVIK_MINOR_VERSION, DALVIK_BUG_VERSION);
192    expandBufAddUtf8String(pReply, (const u1*) tmpBuf);
193    /* JDWP version numbers */
194    expandBufAdd4BE(pReply, 1);        // major
195    expandBufAdd4BE(pReply, 5);        // minor
196    /* VM JRE version */
197    expandBufAddUtf8String(pReply, (const u1*) "1.5.0");  /* e.g. 1.5.0_04 */
198    /* target VM name */
199    expandBufAddUtf8String(pReply, (const u1*) "DalvikVM");
200
201    return ERR_NONE;
202}
203
204/*
205 * Given a class JNI signature (e.g. "Ljava/lang/Error;"), return the
206 * referenceTypeID.  We need to send back more than one if the class has
207 * been loaded by multiple class loaders.
208 */
209static JdwpError handleVM_ClassesBySignature(JdwpState* state,
210    const u1* buf, int dataLen, ExpandBuf* pReply)
211{
212    size_t strLen;
213    char* classDescriptor = readNewUtf8String(&buf, &strLen);
214    ALOGV("  Req for class by signature '%s'", classDescriptor);
215
216    /*
217     * TODO: if a class with the same name has been loaded multiple times
218     * (by different class loaders), we're supposed to return each of them.
219     *
220     * NOTE: this may mangle "className".
221     */
222    u4 numClasses;
223    RefTypeId refTypeId;
224    if (!dvmDbgFindLoadedClassBySignature(classDescriptor, &refTypeId)) {
225        /* not currently loaded */
226        ALOGV("    --> no match!");
227        numClasses = 0;
228    } else {
229        /* just the one */
230        numClasses = 1;
231    }
232
233    expandBufAdd4BE(pReply, numClasses);
234
235    if (numClasses > 0) {
236        u1 typeTag;
237        u4 status;
238
239        /* get class vs. interface and status flags */
240        dvmDbgGetClassInfo(refTypeId, &typeTag, &status, NULL);
241
242        expandBufAdd1(pReply, typeTag);
243        expandBufAddRefTypeId(pReply, refTypeId);
244        expandBufAdd4BE(pReply, status);
245    }
246
247    free(classDescriptor);
248
249    return ERR_NONE;
250}
251
252/*
253 * Handle request for the thread IDs of all running threads.
254 *
255 * We exclude ourselves from the list, because we don't allow ourselves
256 * to be suspended, and that violates some JDWP expectations.
257 */
258static JdwpError handleVM_AllThreads(JdwpState* state,
259    const u1* buf, int dataLen, ExpandBuf* pReply)
260{
261    ObjectId* pThreadIds;
262    u4 threadCount;
263    dvmDbgGetAllThreads(&pThreadIds, &threadCount);
264
265    expandBufAdd4BE(pReply, threadCount);
266
267    ObjectId* walker = pThreadIds;
268    for (u4 i = 0; i < threadCount; i++) {
269        expandBufAddObjectId(pReply, *walker++);
270    }
271
272    free(pThreadIds);
273
274    return ERR_NONE;
275}
276
277/*
278 * List all thread groups that do not have a parent.
279 */
280static JdwpError handleVM_TopLevelThreadGroups(JdwpState* state,
281    const u1* buf, int dataLen, ExpandBuf* pReply)
282{
283    /*
284     * TODO: maintain a list of parentless thread groups in the VM.
285     *
286     * For now, just return "system".  Application threads are created
287     * in "main", which is a child of "system".
288     */
289    u4 groups = 1;
290    expandBufAdd4BE(pReply, groups);
291    //threadGroupId = debugGetMainThreadGroup();
292    //expandBufAdd8BE(pReply, threadGroupId);
293    ObjectId threadGroupId = dvmDbgGetSystemThreadGroupId();
294    expandBufAddObjectId(pReply, threadGroupId);
295
296    return ERR_NONE;
297}
298
299/*
300 * Respond with the sizes of the basic debugger types.
301 *
302 * All IDs are 8 bytes.
303 */
304static JdwpError handleVM_IDSizes(JdwpState* state,
305    const u1* buf, int dataLen, ExpandBuf* pReply)
306{
307    expandBufAdd4BE(pReply, sizeof(FieldId));
308    expandBufAdd4BE(pReply, sizeof(MethodId));
309    expandBufAdd4BE(pReply, sizeof(ObjectId));
310    expandBufAdd4BE(pReply, sizeof(RefTypeId));
311    expandBufAdd4BE(pReply, sizeof(FrameId));
312    return ERR_NONE;
313}
314
315/*
316 * The debugger is politely asking to disconnect.  We're good with that.
317 *
318 * We could resume threads and clean up pinned references, but we can do
319 * that when the TCP connection drops.
320 */
321static JdwpError handleVM_Dispose(JdwpState* state,
322    const u1* buf, int dataLen, ExpandBuf* pReply)
323{
324    return ERR_NONE;
325}
326
327/*
328 * Suspend the execution of the application running in the VM (i.e. suspend
329 * all threads).
330 *
331 * This needs to increment the "suspend count" on all threads.
332 */
333static JdwpError handleVM_Suspend(JdwpState* state,
334    const u1* buf, int dataLen, ExpandBuf* pReply)
335{
336    dvmDbgSuspendVM(false);
337    return ERR_NONE;
338}
339
340/*
341 * Resume execution.  Decrements the "suspend count" of all threads.
342 */
343static JdwpError handleVM_Resume(JdwpState* state,
344    const u1* buf, int dataLen, ExpandBuf* pReply)
345{
346    dvmDbgResumeVM();
347    return ERR_NONE;
348}
349
350/*
351 * The debugger wants the entire VM to exit.
352 */
353static JdwpError handleVM_Exit(JdwpState* state,
354    const u1* buf, int dataLen, ExpandBuf* pReply)
355{
356    u4 exitCode = get4BE(buf);
357
358    ALOGW("Debugger is telling the VM to exit with code=%d", exitCode);
359
360    dvmDbgExit(exitCode);
361    return ERR_NOT_IMPLEMENTED;     // shouldn't get here
362}
363
364/*
365 * Create a new string in the VM and return its ID.
366 *
367 * (Ctrl-Shift-I in Eclipse on an array of objects causes it to create the
368 * string "java.util.Arrays".)
369 */
370static JdwpError handleVM_CreateString(JdwpState* state,
371    const u1* buf, int dataLen, ExpandBuf* pReply)
372{
373    size_t strLen;
374    char* str = readNewUtf8String(&buf, &strLen);
375
376    ALOGV("  Req to create string '%s'", str);
377
378    ObjectId stringId = dvmDbgCreateString(str);
379    if (stringId == 0)
380        return ERR_OUT_OF_MEMORY;
381
382    expandBufAddObjectId(pReply, stringId);
383    return ERR_NONE;
384}
385
386/*
387 * Tell the debugger what we are capable of.
388 */
389static JdwpError handleVM_Capabilities(JdwpState* state,
390    const u1* buf, int dataLen, ExpandBuf* pReply)
391{
392    expandBufAdd1(pReply, false);   /* canWatchFieldModification */
393    expandBufAdd1(pReply, false);   /* canWatchFieldAccess */
394    expandBufAdd1(pReply, false);   /* canGetBytecodes */
395    expandBufAdd1(pReply, true);    /* canGetSyntheticAttribute */
396    expandBufAdd1(pReply, false);   /* canGetOwnedMonitorInfo */
397    expandBufAdd1(pReply, false);   /* canGetCurrentContendedMonitor */
398    expandBufAdd1(pReply, false);   /* canGetMonitorInfo */
399    return ERR_NONE;
400}
401
402/*
403 * Return classpath and bootclasspath.
404 */
405static JdwpError handleVM_ClassPaths(JdwpState* state,
406    const u1* buf, int dataLen, ExpandBuf* pReply)
407{
408    char baseDir[2] = "/";
409
410    /*
411     * TODO: make this real.  Not important for remote debugging, but
412     * might be useful for local debugging.
413     */
414    u4 classPaths = 1;
415    u4 bootClassPaths = 0;
416
417    expandBufAddUtf8String(pReply, (const u1*) baseDir);
418    expandBufAdd4BE(pReply, classPaths);
419    for (u4 i = 0; i < classPaths; i++) {
420        expandBufAddUtf8String(pReply, (const u1*) ".");
421    }
422
423    expandBufAdd4BE(pReply, bootClassPaths);
424    for (u4 i = 0; i < classPaths; i++) {
425        /* add bootclasspath components as strings */
426    }
427
428    return ERR_NONE;
429}
430
431/*
432 * Release a list of object IDs.  (Seen in jdb.)
433 *
434 * Currently does nothing.
435 */
436static JdwpError HandleVM_DisposeObjects(JdwpState* state,
437    const u1* buf, int dataLen, ExpandBuf* pReply)
438{
439    return ERR_NONE;
440}
441
442/*
443 * Tell the debugger what we are capable of.
444 */
445static JdwpError handleVM_CapabilitiesNew(JdwpState* state,
446    const u1* buf, int dataLen, ExpandBuf* pReply)
447{
448    expandBufAdd1(pReply, false);   /* canWatchFieldModification */
449    expandBufAdd1(pReply, false);   /* canWatchFieldAccess */
450    expandBufAdd1(pReply, false);   /* canGetBytecodes */
451    expandBufAdd1(pReply, true);    /* canGetSyntheticAttribute */
452    expandBufAdd1(pReply, false);   /* canGetOwnedMonitorInfo */
453    expandBufAdd1(pReply, false);   /* canGetCurrentContendedMonitor */
454    expandBufAdd1(pReply, false);   /* canGetMonitorInfo */
455    expandBufAdd1(pReply, false);   /* canRedefineClasses */
456    expandBufAdd1(pReply, false);   /* canAddMethod */
457    expandBufAdd1(pReply, false);   /* canUnrestrictedlyRedefineClasses */
458    expandBufAdd1(pReply, false);   /* canPopFrames */
459    expandBufAdd1(pReply, false);   /* canUseInstanceFilters */
460    expandBufAdd1(pReply, false);   /* canGetSourceDebugExtension */
461    expandBufAdd1(pReply, false);   /* canRequestVMDeathEvent */
462    expandBufAdd1(pReply, false);   /* canSetDefaultStratum */
463    expandBufAdd1(pReply, false);   /* 1.6: canGetInstanceInfo */
464    expandBufAdd1(pReply, false);   /* 1.6: canRequestMonitorEvents */
465    expandBufAdd1(pReply, false);   /* 1.6: canGetMonitorFrameInfo */
466    expandBufAdd1(pReply, false);   /* 1.6: canUseSourceNameFilters */
467    expandBufAdd1(pReply, false);   /* 1.6: canGetConstantPool */
468    expandBufAdd1(pReply, false);   /* 1.6: canForceEarlyReturn */
469
470    /* fill in reserved22 through reserved32; note count started at 1 */
471    for (int i = 22; i <= 32; i++)
472        expandBufAdd1(pReply, false);   /* reservedN */
473    return ERR_NONE;
474}
475
476/*
477 * Cough up the complete list of classes.
478 */
479static JdwpError handleVM_AllClassesWithGeneric(JdwpState* state,
480    const u1* buf, int dataLen, ExpandBuf* pReply)
481{
482    u4 numClasses = 0;
483    RefTypeId* classRefBuf = NULL;
484
485    dvmDbgGetClassList(&numClasses, &classRefBuf);
486
487    expandBufAdd4BE(pReply, numClasses);
488
489    for (u4 i = 0; i < numClasses; i++) {
490        static const u1 genericSignature[1] = "";
491        u1 refTypeTag;
492        const char* signature;
493        u4 status;
494
495        dvmDbgGetClassInfo(classRefBuf[i], &refTypeTag, &status, &signature);
496
497        expandBufAdd1(pReply, refTypeTag);
498        expandBufAddRefTypeId(pReply, classRefBuf[i]);
499        expandBufAddUtf8String(pReply, (const u1*) signature);
500        expandBufAddUtf8String(pReply, genericSignature);
501        expandBufAdd4BE(pReply, status);
502    }
503
504    free(classRefBuf);
505
506    return ERR_NONE;
507}
508
509/*
510 * Given a referenceTypeID, return a string with the JNI reference type
511 * signature (e.g. "Ljava/lang/Error;").
512 */
513static JdwpError handleRT_Signature(JdwpState* state,
514    const u1* buf, int dataLen, ExpandBuf* pReply)
515{
516    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
517
518    ALOGV("  Req for signature of refTypeId=0x%llx", refTypeId);
519    const char* signature = dvmDbgGetSignature(refTypeId);
520    expandBufAddUtf8String(pReply, (const u1*) signature);
521
522    return ERR_NONE;
523}
524
525/*
526 * Return the modifiers (a/k/a access flags) for a reference type.
527 */
528static JdwpError handleRT_Modifiers(JdwpState* state,
529    const u1* buf, int dataLen, ExpandBuf* pReply)
530{
531    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
532    u4 modBits = dvmDbgGetAccessFlags(refTypeId);
533
534    expandBufAdd4BE(pReply, modBits);
535
536    return ERR_NONE;
537}
538
539/*
540 * Get values from static fields in a reference type.
541 */
542static JdwpError handleRT_GetValues(JdwpState* state,
543    const u1* buf, int dataLen, ExpandBuf* pReply)
544{
545    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
546    u4 numFields = read4BE(&buf);
547
548    ALOGV("  RT_GetValues %u:", numFields);
549
550    expandBufAdd4BE(pReply, numFields);
551    for (u4 i = 0; i < numFields; i++) {
552        FieldId fieldId = dvmReadFieldId(&buf);
553        dvmDbgGetStaticFieldValue(refTypeId, fieldId, pReply);
554    }
555
556    return ERR_NONE;
557}
558
559/*
560 * Get the name of the source file in which a reference type was declared.
561 */
562static JdwpError handleRT_SourceFile(JdwpState* state,
563    const u1* buf, int dataLen, ExpandBuf* pReply)
564{
565    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
566
567    const char* fileName = dvmDbgGetSourceFile(refTypeId);
568    if (fileName != NULL) {
569        expandBufAddUtf8String(pReply, (const u1*) fileName);
570        return ERR_NONE;
571    } else {
572        return ERR_ABSENT_INFORMATION;
573    }
574}
575
576/*
577 * Return the current status of the reference type.
578 */
579static JdwpError handleRT_Status(JdwpState* state,
580    const u1* buf, int dataLen, ExpandBuf* pReply)
581{
582    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
583
584    /* get status flags */
585    u1 typeTag;
586    u4 status;
587    dvmDbgGetClassInfo(refTypeId, &typeTag, &status, NULL);
588    expandBufAdd4BE(pReply, status);
589    return ERR_NONE;
590}
591
592/*
593 * Return interfaces implemented directly by this class.
594 */
595static JdwpError handleRT_Interfaces(JdwpState* state,
596    const u1* buf, int dataLen, ExpandBuf* pReply)
597{
598    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
599
600    ALOGV("  Req for interfaces in %llx (%s)", refTypeId,
601        dvmDbgGetClassDescriptor(refTypeId));
602
603    dvmDbgOutputAllInterfaces(refTypeId, pReply);
604
605    return ERR_NONE;
606}
607
608/*
609 * Return the class object corresponding to this type.
610 */
611static JdwpError handleRT_ClassObject(JdwpState* state,
612    const u1* buf, int dataLen, ExpandBuf* pReply)
613{
614    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
615    ObjectId classObjId = dvmDbgGetClassObject(refTypeId);
616
617    ALOGV("  RefTypeId %llx -> ObjectId %llx", refTypeId, classObjId);
618
619    expandBufAddObjectId(pReply, classObjId);
620
621    return ERR_NONE;
622}
623
624/*
625 * Returns the value of the SourceDebugExtension attribute.
626 *
627 * JDB seems interested, but DEX files don't currently support this.
628 */
629static JdwpError handleRT_SourceDebugExtension(JdwpState* state,
630    const u1* buf, int dataLen, ExpandBuf* pReply)
631{
632    /* referenceTypeId in, string out */
633    return ERR_ABSENT_INFORMATION;
634}
635
636/*
637 * Like RT_Signature but with the possibility of a "generic signature".
638 */
639static JdwpError handleRT_SignatureWithGeneric(JdwpState* state,
640    const u1* buf, int dataLen, ExpandBuf* pReply)
641{
642    static const u1 genericSignature[1] = "";
643
644    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
645
646    ALOGV("  Req for signature of refTypeId=0x%llx", refTypeId);
647    const char* signature = dvmDbgGetSignature(refTypeId);
648    if (signature != NULL) {
649        expandBufAddUtf8String(pReply, (const u1*) signature);
650    } else {
651        ALOGW("No signature for refTypeId=0x%llx", refTypeId);
652        expandBufAddUtf8String(pReply, (const u1*) "Lunknown;");
653    }
654    expandBufAddUtf8String(pReply, genericSignature);
655
656    return ERR_NONE;
657}
658
659/*
660 * Return the instance of java.lang.ClassLoader that loaded the specified
661 * reference type, or null if it was loaded by the system loader.
662 */
663static JdwpError handleRT_ClassLoader(JdwpState* state,
664    const u1* buf, int dataLen, ExpandBuf* pReply)
665{
666    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
667
668    expandBufAddObjectId(pReply, dvmDbgGetClassLoader(refTypeId));
669
670    return ERR_NONE;
671}
672
673/*
674 * Given a referenceTypeId, return a block of stuff that describes the
675 * fields declared by a class.
676 */
677static JdwpError handleRT_FieldsWithGeneric(JdwpState* state,
678    const u1* buf, int dataLen, ExpandBuf* pReply)
679{
680    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
681    ALOGV("  Req for fields in refTypeId=0x%llx", refTypeId);
682    ALOGV("  --> '%s'", dvmDbgGetSignature(refTypeId));
683
684    dvmDbgOutputAllFields(refTypeId, true, pReply);
685
686    return ERR_NONE;
687}
688
689/*
690 * Given a referenceTypeID, return a block of goodies describing the
691 * methods declared by a class.
692 */
693static JdwpError handleRT_MethodsWithGeneric(JdwpState* state,
694    const u1* buf, int dataLen, ExpandBuf* pReply)
695{
696    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
697
698    ALOGV("  Req for methods in refTypeId=0x%llx", refTypeId);
699    ALOGV("  --> '%s'", dvmDbgGetSignature(refTypeId));
700
701    dvmDbgOutputAllMethods(refTypeId, true, pReply);
702
703    return ERR_NONE;
704}
705
706/*
707 * Return the immediate superclass of a class.
708 */
709static JdwpError handleCT_Superclass(JdwpState* state,
710    const u1* buf, int dataLen, ExpandBuf* pReply)
711{
712    RefTypeId classId = dvmReadRefTypeId(&buf);
713
714    RefTypeId superClassId = dvmDbgGetSuperclass(classId);
715
716    expandBufAddRefTypeId(pReply, superClassId);
717
718    return ERR_NONE;
719}
720
721/*
722 * Set static class values.
723 */
724static JdwpError handleCT_SetValues(JdwpState* state,
725    const u1* buf, int dataLen, ExpandBuf* pReply)
726{
727    RefTypeId classId = dvmReadRefTypeId(&buf);
728    u4 values = read4BE(&buf);
729
730    ALOGV("  Req to set %d values in classId=%llx", values, classId);
731
732    for (u4 i = 0; i < values; i++) {
733        FieldId fieldId = dvmReadFieldId(&buf);
734        u1 fieldTag = dvmDbgGetStaticFieldBasicTag(classId, fieldId);
735        int width = dvmDbgGetTagWidth(fieldTag);
736        u8 value = jdwpReadValue(&buf, width);
737
738        ALOGV("    --> field=%x tag=%c -> %lld", fieldId, fieldTag, value);
739        dvmDbgSetStaticFieldValue(classId, fieldId, value, width);
740    }
741
742    return ERR_NONE;
743}
744
745/*
746 * Invoke a static method.
747 *
748 * Example: Eclipse sometimes uses java/lang/Class.forName(String s) on
749 * values in the "variables" display.
750 */
751static JdwpError handleCT_InvokeMethod(JdwpState* state,
752    const u1* buf, int dataLen, ExpandBuf* pReply)
753{
754    RefTypeId classId = dvmReadRefTypeId(&buf);
755    ObjectId threadId = dvmReadObjectId(&buf);
756    MethodId methodId = dvmReadMethodId(&buf);
757
758    return finishInvoke(state, buf, dataLen, pReply,
759            threadId, 0, classId, methodId, false);
760}
761
762/*
763 * Create a new object of the requested type, and invoke the specified
764 * constructor.
765 *
766 * Example: in IntelliJ, create a watch on "new String(myByteArray)" to
767 * see the contents of a byte[] as a string.
768 */
769static JdwpError handleCT_NewInstance(JdwpState* state,
770    const u1* buf, int dataLen, ExpandBuf* pReply)
771{
772    RefTypeId classId = dvmReadRefTypeId(&buf);
773    ObjectId threadId = dvmReadObjectId(&buf);
774    MethodId methodId = dvmReadMethodId(&buf);
775
776    ALOGV("Creating instance of %s", dvmDbgGetClassDescriptor(classId));
777    ObjectId objectId = dvmDbgCreateObject(classId);
778    if (objectId == 0)
779        return ERR_OUT_OF_MEMORY;
780
781    return finishInvoke(state, buf, dataLen, pReply,
782            threadId, objectId, classId, methodId, true);
783}
784
785/*
786 * Create a new array object of the requested type and length.
787 */
788static JdwpError handleAT_newInstance(JdwpState* state,
789    const u1* buf, int dataLen, ExpandBuf* pReply)
790{
791    RefTypeId arrayTypeId = dvmReadRefTypeId(&buf);
792    u4 length = read4BE(&buf);
793
794    ALOGV("Creating array %s[%u]",
795        dvmDbgGetClassDescriptor(arrayTypeId), length);
796    ObjectId objectId = dvmDbgCreateArrayObject(arrayTypeId, length);
797    if (objectId == 0)
798        return ERR_OUT_OF_MEMORY;
799
800    expandBufAdd1(pReply, JT_ARRAY);
801    expandBufAddObjectId(pReply, objectId);
802    return ERR_NONE;
803}
804
805/*
806 * Return line number information for the method, if present.
807 */
808static JdwpError handleM_LineTable(JdwpState* state,
809    const u1* buf, int dataLen, ExpandBuf* pReply)
810{
811    RefTypeId refTypeId = dvmReadRefTypeId(&buf);
812    MethodId methodId = dvmReadMethodId(&buf);
813
814    ALOGV("  Req for line table in %s.%s",
815        dvmDbgGetClassDescriptor(refTypeId),
816        dvmDbgGetMethodName(refTypeId,methodId));
817
818    dvmDbgOutputLineTable(refTypeId, methodId, pReply);
819
820    return ERR_NONE;
821}
822
823/*
824 * Pull out the LocalVariableTable goodies.
825 */
826static JdwpError handleM_VariableTableWithGeneric(JdwpState* state,
827    const u1* buf, int dataLen, ExpandBuf* pReply)
828{
829    RefTypeId classId = dvmReadRefTypeId(&buf);
830    MethodId methodId = dvmReadMethodId(&buf);
831
832    ALOGV("  Req for LocalVarTab in class=%s method=%s",
833        dvmDbgGetClassDescriptor(classId),
834        dvmDbgGetMethodName(classId, methodId));
835
836    /*
837     * We could return ERR_ABSENT_INFORMATION here if the DEX file was
838     * built without local variable information.  That will cause Eclipse
839     * to make a best-effort attempt at displaying local variables
840     * anonymously.  However, the attempt isn't very good, so we're probably
841     * better off just not showing anything.
842     */
843    dvmDbgOutputVariableTable(classId, methodId, true, pReply);
844    return ERR_NONE;
845}
846
847/*
848 * Given an object reference, return the runtime type of the object
849 * (class or array).
850 *
851 * This can get called on different things, e.g. threadId gets
852 * passed in here.
853 */
854static JdwpError handleOR_ReferenceType(JdwpState* state,
855    const u1* buf, int dataLen, ExpandBuf* pReply)
856{
857    ObjectId objectId = dvmReadObjectId(&buf);
858    ALOGV("  Req for type of objectId=0x%llx", objectId);
859
860    u1 refTypeTag;
861    RefTypeId typeId;
862    dvmDbgGetObjectType(objectId, &refTypeTag, &typeId);
863
864    expandBufAdd1(pReply, refTypeTag);
865    expandBufAddRefTypeId(pReply, typeId);
866
867    return ERR_NONE;
868}
869
870/*
871 * Get values from the fields of an object.
872 */
873static JdwpError handleOR_GetValues(JdwpState* state,
874    const u1* buf, int dataLen, ExpandBuf* pReply)
875{
876    ObjectId objectId = dvmReadObjectId(&buf);
877    u4 numFields = read4BE(&buf);
878
879    ALOGV("  Req for %d fields from objectId=0x%llx", numFields, objectId);
880
881    expandBufAdd4BE(pReply, numFields);
882
883    for (u4 i = 0; i < numFields; i++) {
884        FieldId fieldId = dvmReadFieldId(&buf);
885        dvmDbgGetFieldValue(objectId, fieldId, pReply);
886    }
887
888    return ERR_NONE;
889}
890
891/*
892 * Set values in the fields of an object.
893 */
894static JdwpError handleOR_SetValues(JdwpState* state,
895    const u1* buf, int dataLen, ExpandBuf* pReply)
896{
897    ObjectId objectId = dvmReadObjectId(&buf);
898    u4 numFields = read4BE(&buf);
899
900    ALOGV("  Req to set %d fields in objectId=0x%llx", numFields, objectId);
901
902    for (u4 i = 0; i < numFields; i++) {
903        FieldId fieldId = dvmReadFieldId(&buf);
904
905        u1 fieldTag = dvmDbgGetFieldBasicTag(objectId, fieldId);
906        int width = dvmDbgGetTagWidth(fieldTag);
907        u8 value = jdwpReadValue(&buf, width);
908
909        ALOGV("    --> fieldId=%x tag='%c'(%d) value=%lld",
910            fieldId, fieldTag, width, value);
911
912        dvmDbgSetFieldValue(objectId, fieldId, value, width);
913    }
914
915    return ERR_NONE;
916}
917
918/*
919 * Invoke an instance method.  The invocation must occur in the specified
920 * thread, which must have been suspended by an event.
921 *
922 * The call is synchronous.  All threads in the VM are resumed, unless the
923 * SINGLE_THREADED flag is set.
924 *
925 * If you ask Eclipse to "inspect" an object (or ask JDB to "print" an
926 * object), it will try to invoke the object's toString() function.  This
927 * feature becomes crucial when examining ArrayLists with Eclipse.
928 */
929static JdwpError handleOR_InvokeMethod(JdwpState* state,
930    const u1* buf, int dataLen, ExpandBuf* pReply)
931{
932    ObjectId objectId = dvmReadObjectId(&buf);
933    ObjectId threadId = dvmReadObjectId(&buf);
934    RefTypeId classId = dvmReadRefTypeId(&buf);
935    MethodId methodId = dvmReadMethodId(&buf);
936
937    return finishInvoke(state, buf, dataLen, pReply,
938            threadId, objectId, classId, methodId, false);
939}
940
941/*
942 * Disable garbage collection of the specified object.
943 */
944static JdwpError handleOR_DisableCollection(JdwpState* state,
945    const u1* buf, int dataLen, ExpandBuf* pReply)
946{
947    // this is currently a no-op
948    return ERR_NONE;
949}
950
951/*
952 * Enable garbage collection of the specified object.
953 */
954static JdwpError handleOR_EnableCollection(JdwpState* state,
955    const u1* buf, int dataLen, ExpandBuf* pReply)
956{
957    // this is currently a no-op
958    return ERR_NONE;
959}
960
961/*
962 * Determine whether an object has been garbage collected.
963 */
964static JdwpError handleOR_IsCollected(JdwpState* state,
965    const u1* buf, int dataLen, ExpandBuf* pReply)
966{
967    ObjectId objectId;
968
969    objectId = dvmReadObjectId(&buf);
970    ALOGV("  Req IsCollected(0x%llx)", objectId);
971
972    // TODO: currently returning false; must integrate with GC
973    expandBufAdd1(pReply, 0);
974
975    return ERR_NONE;
976}
977
978/*
979 * Return the string value in a string object.
980 */
981static JdwpError handleSR_Value(JdwpState* state,
982    const u1* buf, int dataLen, ExpandBuf* pReply)
983{
984    ObjectId stringObject = dvmReadObjectId(&buf);
985    char* str = dvmDbgStringToUtf8(stringObject);
986
987    ALOGV("  Req for str %llx --> '%s'", stringObject, str);
988
989    expandBufAddUtf8String(pReply, (u1*) str);
990    free(str);
991
992    return ERR_NONE;
993}
994
995/*
996 * Return a thread's name.
997 */
998static JdwpError handleTR_Name(JdwpState* state,
999    const u1* buf, int dataLen, ExpandBuf* pReply)
1000{
1001    ObjectId threadId = dvmReadObjectId(&buf);
1002
1003    ALOGV("  Req for name of thread 0x%llx", threadId);
1004    char* name = dvmDbgGetThreadName(threadId);
1005    if (name == NULL)
1006        return ERR_INVALID_THREAD;
1007
1008    expandBufAddUtf8String(pReply, (u1*) name);
1009    free(name);
1010
1011    return ERR_NONE;
1012}
1013
1014/*
1015 * Suspend the specified thread.
1016 *
1017 * It's supposed to remain suspended even if interpreted code wants to
1018 * resume it; only the JDI is allowed to resume it.
1019 */
1020static JdwpError handleTR_Suspend(JdwpState* state,
1021    const u1* buf, int dataLen, ExpandBuf* pReply)
1022{
1023    ObjectId threadId = dvmReadObjectId(&buf);
1024
1025    if (threadId == dvmDbgGetThreadSelfId()) {
1026        ALOGI("  Warning: ignoring request to suspend self");
1027        return ERR_THREAD_NOT_SUSPENDED;
1028    }
1029    ALOGV("  Req to suspend thread 0x%llx", threadId);
1030
1031    dvmDbgSuspendThread(threadId);
1032
1033    return ERR_NONE;
1034}
1035
1036/*
1037 * Resume the specified thread.
1038 */
1039static JdwpError handleTR_Resume(JdwpState* state,
1040    const u1* buf, int dataLen, ExpandBuf* pReply)
1041{
1042    ObjectId threadId = dvmReadObjectId(&buf);
1043
1044    if (threadId == dvmDbgGetThreadSelfId()) {
1045        ALOGI("  Warning: ignoring request to resume self");
1046        return ERR_NONE;
1047    }
1048    ALOGV("  Req to resume thread 0x%llx", threadId);
1049
1050    dvmDbgResumeThread(threadId);
1051
1052    return ERR_NONE;
1053}
1054
1055/*
1056 * Return status of specified thread.
1057 */
1058static JdwpError handleTR_Status(JdwpState* state,
1059    const u1* buf, int dataLen, ExpandBuf* pReply)
1060{
1061    ObjectId threadId = dvmReadObjectId(&buf);
1062
1063    ALOGV("  Req for status of thread 0x%llx", threadId);
1064
1065    u4 threadStatus;
1066    u4 suspendStatus;
1067    if (!dvmDbgGetThreadStatus(threadId, &threadStatus, &suspendStatus))
1068        return ERR_INVALID_THREAD;
1069
1070    ALOGV("    --> %s, %s",
1071        dvmJdwpThreadStatusStr((JdwpThreadStatus) threadStatus),
1072        dvmJdwpSuspendStatusStr((JdwpSuspendStatus) suspendStatus));
1073
1074    expandBufAdd4BE(pReply, threadStatus);
1075    expandBufAdd4BE(pReply, suspendStatus);
1076
1077    return ERR_NONE;
1078}
1079
1080/*
1081 * Return the thread group that the specified thread is a member of.
1082 */
1083static JdwpError handleTR_ThreadGroup(JdwpState* state,
1084    const u1* buf, int dataLen, ExpandBuf* pReply)
1085{
1086    ObjectId threadId = dvmReadObjectId(&buf);
1087
1088    /* currently not handling these */
1089    ObjectId threadGroupId = dvmDbgGetThreadGroup(threadId);
1090    expandBufAddObjectId(pReply, threadGroupId);
1091
1092    return ERR_NONE;
1093}
1094
1095/*
1096 * Return the current call stack of a suspended thread.
1097 *
1098 * If the thread isn't suspended, the error code isn't defined, but should
1099 * be THREAD_NOT_SUSPENDED.
1100 */
1101static JdwpError handleTR_Frames(JdwpState* state,
1102    const u1* buf, int dataLen, ExpandBuf* pReply)
1103{
1104    ObjectId threadId = dvmReadObjectId(&buf);
1105    u4 startFrame = read4BE(&buf);
1106    u4 length = read4BE(&buf);
1107
1108    if (!dvmDbgThreadExists(threadId))
1109        return ERR_INVALID_THREAD;
1110    if (!dvmDbgIsSuspended(threadId)) {
1111        ALOGV("  Rejecting req for frames in running thread '%s' (%llx)",
1112            dvmDbgGetThreadName(threadId), threadId);
1113        return ERR_THREAD_NOT_SUSPENDED;
1114    }
1115
1116    int frameCount = dvmDbgGetThreadFrameCount(threadId);
1117
1118    ALOGV("  Request for frames: threadId=%llx start=%d length=%d [count=%d]",
1119        threadId, startFrame, length, frameCount);
1120    if (frameCount <= 0)
1121        return ERR_THREAD_NOT_SUSPENDED;    /* == 0 means 100% native */
1122
1123    if (length == (u4) -1)
1124        length = frameCount;
1125    assert((int) startFrame >= 0 && (int) startFrame < frameCount);
1126    assert((int) (startFrame + length) <= frameCount);
1127
1128    u4 frames = length;
1129    expandBufAdd4BE(pReply, frames);
1130    for (u4 i = startFrame; i < (startFrame+length); i++) {
1131        FrameId frameId;
1132        JdwpLocation loc;
1133
1134        dvmDbgGetThreadFrame(threadId, i, &frameId, &loc);
1135
1136        expandBufAdd8BE(pReply, frameId);
1137        dvmJdwpAddLocation(pReply, &loc);
1138
1139        LOGVV("    Frame %d: id=%llx loc={type=%d cls=%llx mth=%x loc=%llx}",
1140            i, frameId, loc.typeTag, loc.classId, loc.methodId, loc.idx);
1141    }
1142
1143    return ERR_NONE;
1144}
1145
1146/*
1147 * Returns the #of frames on the specified thread, which must be suspended.
1148 */
1149static JdwpError handleTR_FrameCount(JdwpState* state,
1150    const u1* buf, int dataLen, ExpandBuf* pReply)
1151{
1152    ObjectId threadId = dvmReadObjectId(&buf);
1153
1154    if (!dvmDbgThreadExists(threadId))
1155        return ERR_INVALID_THREAD;
1156    if (!dvmDbgIsSuspended(threadId)) {
1157        ALOGV("  Rejecting req for frames in running thread '%s' (%llx)",
1158            dvmDbgGetThreadName(threadId), threadId);
1159        return ERR_THREAD_NOT_SUSPENDED;
1160    }
1161
1162    int frameCount = dvmDbgGetThreadFrameCount(threadId);
1163    if (frameCount < 0)
1164        return ERR_INVALID_THREAD;
1165    expandBufAdd4BE(pReply, (u4)frameCount);
1166
1167    return ERR_NONE;
1168}
1169
1170/*
1171 * Get the monitor that the thread is waiting on.
1172 */
1173static JdwpError handleTR_CurrentContendedMonitor(JdwpState* state,
1174    const u1* buf, int dataLen, ExpandBuf* pReply)
1175{
1176    ObjectId threadId;
1177
1178    threadId = dvmReadObjectId(&buf);
1179
1180    // TODO: create an Object to represent the monitor (we're currently
1181    // just using a raw Monitor struct in the VM)
1182
1183    return ERR_NOT_IMPLEMENTED;
1184}
1185
1186/*
1187 * Return the suspend count for the specified thread.
1188 *
1189 * (The thread *might* still be running -- it might not have examined
1190 * its suspend count recently.)
1191 */
1192static JdwpError handleTR_SuspendCount(JdwpState* state,
1193    const u1* buf, int dataLen, ExpandBuf* pReply)
1194{
1195    ObjectId threadId = dvmReadObjectId(&buf);
1196
1197    u4 suspendCount = dvmDbgGetThreadSuspendCount(threadId);
1198    expandBufAdd4BE(pReply, suspendCount);
1199
1200    return ERR_NONE;
1201}
1202
1203/*
1204 * Return the name of a thread group.
1205 *
1206 * The Eclipse debugger recognizes "main" and "system" as special.
1207 */
1208static JdwpError handleTGR_Name(JdwpState* state,
1209    const u1* buf, int dataLen, ExpandBuf* pReply)
1210{
1211    ObjectId threadGroupId = dvmReadObjectId(&buf);
1212    ALOGV("  Req for name of threadGroupId=0x%llx", threadGroupId);
1213
1214    char* name = dvmDbgGetThreadGroupName(threadGroupId);
1215    if (name != NULL)
1216        expandBufAddUtf8String(pReply, (u1*) name);
1217    else {
1218        expandBufAddUtf8String(pReply, (u1*) "BAD-GROUP-ID");
1219        ALOGW("bad thread group ID");
1220    }
1221
1222    free(name);
1223
1224    return ERR_NONE;
1225}
1226
1227/*
1228 * Returns the thread group -- if any -- that contains the specified
1229 * thread group.
1230 */
1231static JdwpError handleTGR_Parent(JdwpState* state,
1232    const u1* buf, int dataLen, ExpandBuf* pReply)
1233{
1234    ObjectId groupId = dvmReadObjectId(&buf);
1235
1236    ObjectId parentGroup = dvmDbgGetThreadGroupParent(groupId);
1237    expandBufAddObjectId(pReply, parentGroup);
1238
1239    return ERR_NONE;
1240}
1241
1242/*
1243 * Return the active threads and thread groups that are part of the
1244 * specified thread group.
1245 */
1246static JdwpError handleTGR_Children(JdwpState* state,
1247    const u1* buf, int dataLen, ExpandBuf* pReply)
1248{
1249    ObjectId threadGroupId = dvmReadObjectId(&buf);
1250    ALOGV("  Req for threads in threadGroupId=0x%llx", threadGroupId);
1251
1252    ObjectId* pThreadIds;
1253    u4 threadCount;
1254    dvmDbgGetThreadGroupThreads(threadGroupId, &pThreadIds, &threadCount);
1255
1256    expandBufAdd4BE(pReply, threadCount);
1257
1258    for (u4 i = 0; i < threadCount; i++)
1259        expandBufAddObjectId(pReply, pThreadIds[i]);
1260    free(pThreadIds);
1261
1262    /*
1263     * TODO: finish support for child groups
1264     *
1265     * For now, just show that "main" is a child of "system".
1266     */
1267    if (threadGroupId == dvmDbgGetSystemThreadGroupId()) {
1268        expandBufAdd4BE(pReply, 1);
1269        expandBufAddObjectId(pReply, dvmDbgGetMainThreadGroupId());
1270    } else {
1271        expandBufAdd4BE(pReply, 0);
1272    }
1273
1274    return ERR_NONE;
1275}
1276
1277/*
1278 * Return the #of components in the array.
1279 */
1280static JdwpError handleAR_Length(JdwpState* state,
1281    const u1* buf, int dataLen, ExpandBuf* pReply)
1282{
1283    ObjectId arrayId = dvmReadObjectId(&buf);
1284    ALOGV("  Req for length of array 0x%llx", arrayId);
1285
1286    u4 arrayLength = dvmDbgGetArrayLength(arrayId);
1287
1288    ALOGV("    --> %d", arrayLength);
1289
1290    expandBufAdd4BE(pReply, arrayLength);
1291
1292    return ERR_NONE;
1293}
1294
1295/*
1296 * Return the values from an array.
1297 */
1298static JdwpError handleAR_GetValues(JdwpState* state,
1299    const u1* buf, int dataLen, ExpandBuf* pReply)
1300{
1301    ObjectId arrayId = dvmReadObjectId(&buf);
1302    u4 firstIndex = read4BE(&buf);
1303    u4 length = read4BE(&buf);
1304
1305    u1 tag = dvmDbgGetArrayElementTag(arrayId);
1306    ALOGV("  Req for array values 0x%llx first=%d len=%d (elem tag=%c)",
1307        arrayId, firstIndex, length, tag);
1308
1309    expandBufAdd1(pReply, tag);
1310    expandBufAdd4BE(pReply, length);
1311
1312    if (!dvmDbgOutputArray(arrayId, firstIndex, length, pReply))
1313        return ERR_INVALID_LENGTH;
1314
1315    return ERR_NONE;
1316}
1317
1318/*
1319 * Set values in an array.
1320 */
1321static JdwpError handleAR_SetValues(JdwpState* state,
1322    const u1* buf, int dataLen, ExpandBuf* pReply)
1323{
1324    ObjectId arrayId = dvmReadObjectId(&buf);
1325    u4 firstIndex = read4BE(&buf);
1326    u4 values = read4BE(&buf);
1327
1328    ALOGV("  Req to set array values 0x%llx first=%d count=%d",
1329        arrayId, firstIndex, values);
1330
1331    if (!dvmDbgSetArrayElements(arrayId, firstIndex, values, buf))
1332        return ERR_INVALID_LENGTH;
1333
1334    return ERR_NONE;
1335}
1336
1337/*
1338 * Return the set of classes visible to a class loader.  All classes which
1339 * have the class loader as a defining or initiating loader are returned.
1340 */
1341static JdwpError handleCLR_VisibleClasses(JdwpState* state,
1342    const u1* buf, int dataLen, ExpandBuf* pReply)
1343{
1344    ObjectId classLoaderObject;
1345    u4 numClasses = 0;
1346    RefTypeId* classRefBuf = NULL;
1347    int i;
1348
1349    classLoaderObject = dvmReadObjectId(&buf);
1350
1351    dvmDbgGetVisibleClassList(classLoaderObject, &numClasses, &classRefBuf);
1352
1353    expandBufAdd4BE(pReply, numClasses);
1354    for (i = 0; i < (int) numClasses; i++) {
1355        u1 refTypeTag;
1356
1357        refTypeTag = dvmDbgGetClassObjectType(classRefBuf[i]);
1358
1359        expandBufAdd1(pReply, refTypeTag);
1360        expandBufAddRefTypeId(pReply, classRefBuf[i]);
1361    }
1362
1363    return ERR_NONE;
1364}
1365
1366/*
1367 * Set an event trigger.
1368 *
1369 * Reply with a requestID.
1370 */
1371static JdwpError handleER_Set(JdwpState* state,
1372    const u1* buf, int dataLen, ExpandBuf* pReply)
1373{
1374    const u1* origBuf = buf;
1375
1376    u1 eventKind = read1(&buf);
1377    u1 suspendPolicy = read1(&buf);
1378    u4 modifierCount = read4BE(&buf);
1379
1380    LOGVV("  Set(kind=%s(%u) suspend=%s(%u) mods=%u)",
1381        dvmJdwpEventKindStr(eventKind), eventKind,
1382        dvmJdwpSuspendPolicyStr(suspendPolicy), suspendPolicy,
1383        modifierCount);
1384
1385    assert(modifierCount < 256);    /* reasonableness check */
1386
1387    JdwpEvent* pEvent = dvmJdwpEventAlloc(modifierCount);
1388    pEvent->eventKind = static_cast<JdwpEventKind>(eventKind);
1389    pEvent->suspendPolicy = static_cast<JdwpSuspendPolicy>(suspendPolicy);
1390    pEvent->modCount = modifierCount;
1391
1392    /*
1393     * Read modifiers.  Ordering may be significant (see explanation of Count
1394     * mods in JDWP doc).
1395     */
1396    for (u4 idx = 0; idx < modifierCount; idx++) {
1397        u1 modKind = read1(&buf);
1398
1399        pEvent->mods[idx].modKind = modKind;
1400
1401        switch (modKind) {
1402        case MK_COUNT:          /* report once, when "--count" reaches 0 */
1403            {
1404                u4 count = read4BE(&buf);
1405                LOGVV("    Count: %u", count);
1406                if (count == 0)
1407                    return ERR_INVALID_COUNT;
1408                pEvent->mods[idx].count.count = count;
1409            }
1410            break;
1411        case MK_CONDITIONAL:    /* conditional on expression) */
1412            {
1413                u4 exprId = read4BE(&buf);
1414                LOGVV("    Conditional: %d", exprId);
1415                pEvent->mods[idx].conditional.exprId = exprId;
1416            }
1417            break;
1418        case MK_THREAD_ONLY:    /* only report events in specified thread */
1419            {
1420                ObjectId threadId = dvmReadObjectId(&buf);
1421                LOGVV("    ThreadOnly: %llx", threadId);
1422                pEvent->mods[idx].threadOnly.threadId = threadId;
1423            }
1424            break;
1425        case MK_CLASS_ONLY:     /* for ClassPrepare, MethodEntry */
1426            {
1427                RefTypeId clazzId = dvmReadRefTypeId(&buf);
1428                LOGVV("    ClassOnly: %llx (%s)",
1429                    clazzId, dvmDbgGetClassDescriptor(clazzId));
1430                pEvent->mods[idx].classOnly.refTypeId = clazzId;
1431            }
1432            break;
1433        case MK_CLASS_MATCH:    /* restrict events to matching classes */
1434            {
1435                char* pattern;
1436                size_t strLen;
1437
1438                pattern = readNewUtf8String(&buf, &strLen);
1439                LOGVV("    ClassMatch: '%s'", pattern);
1440                /* pattern is "java.foo.*", we want "java/foo/ *" */
1441                pEvent->mods[idx].classMatch.classPattern =
1442                    dvmDotToSlash(pattern);
1443                free(pattern);
1444            }
1445            break;
1446        case MK_CLASS_EXCLUDE:  /* restrict events to non-matching classes */
1447            {
1448                char* pattern;
1449                size_t strLen;
1450
1451                pattern = readNewUtf8String(&buf, &strLen);
1452                LOGVV("    ClassExclude: '%s'", pattern);
1453                pEvent->mods[idx].classExclude.classPattern =
1454                    dvmDotToSlash(pattern);
1455                free(pattern);
1456            }
1457            break;
1458        case MK_LOCATION_ONLY:  /* restrict certain events based on loc */
1459            {
1460                JdwpLocation loc;
1461
1462                jdwpReadLocation(&buf, &loc);
1463                LOGVV("    LocationOnly: typeTag=%d classId=%llx methodId=%x idx=%llx",
1464                    loc.typeTag, loc.classId, loc.methodId, loc.idx);
1465                pEvent->mods[idx].locationOnly.loc = loc;
1466            }
1467            break;
1468        case MK_EXCEPTION_ONLY: /* modifies EK_EXCEPTION events */
1469            {
1470                RefTypeId exceptionOrNull;      /* null == all exceptions */
1471                u1 caught, uncaught;
1472
1473                exceptionOrNull = dvmReadRefTypeId(&buf);
1474                caught = read1(&buf);
1475                uncaught = read1(&buf);
1476                LOGVV("    ExceptionOnly: type=%llx(%s) caught=%d uncaught=%d",
1477                    exceptionOrNull, (exceptionOrNull == 0) ? "null"
1478                        : dvmDbgGetClassDescriptor(exceptionOrNull),
1479                    caught, uncaught);
1480
1481                pEvent->mods[idx].exceptionOnly.refTypeId = exceptionOrNull;
1482                pEvent->mods[idx].exceptionOnly.caught = caught;
1483                pEvent->mods[idx].exceptionOnly.uncaught = uncaught;
1484            }
1485            break;
1486        case MK_FIELD_ONLY:     /* for field access/mod events */
1487            {
1488                RefTypeId declaring = dvmReadRefTypeId(&buf);
1489                FieldId fieldId = dvmReadFieldId(&buf);
1490                LOGVV("    FieldOnly: %llx %x", declaring, fieldId);
1491                pEvent->mods[idx].fieldOnly.refTypeId = declaring;
1492                pEvent->mods[idx].fieldOnly.fieldId = fieldId;
1493            }
1494            break;
1495        case MK_STEP:           /* for use with EK_SINGLE_STEP */
1496            {
1497                ObjectId threadId;
1498                u4 size, depth;
1499
1500                threadId = dvmReadObjectId(&buf);
1501                size = read4BE(&buf);
1502                depth = read4BE(&buf);
1503                LOGVV("    Step: thread=%llx size=%s depth=%s",
1504                    threadId, dvmJdwpStepSizeStr(size),
1505                    dvmJdwpStepDepthStr(depth));
1506
1507                pEvent->mods[idx].step.threadId = threadId;
1508                pEvent->mods[idx].step.size = size;
1509                pEvent->mods[idx].step.depth = depth;
1510            }
1511            break;
1512        case MK_INSTANCE_ONLY:  /* report events related to a specific obj */
1513            {
1514                ObjectId instance = dvmReadObjectId(&buf);
1515                LOGVV("    InstanceOnly: %llx", instance);
1516                pEvent->mods[idx].instanceOnly.objectId = instance;
1517            }
1518            break;
1519        default:
1520            ALOGW("GLITCH: unsupported modKind=%d", modKind);
1521            break;
1522        }
1523    }
1524
1525    /*
1526     * Make sure we consumed all data.  It is possible that the remote side
1527     * has sent us bad stuff, but for now we blame ourselves.
1528     */
1529    if (buf != origBuf + dataLen) {
1530        ALOGW("GLITCH: dataLen is %d, we have consumed %d", dataLen,
1531            (int) (buf - origBuf));
1532    }
1533
1534    /*
1535     * We reply with an integer "requestID".
1536     */
1537    u4 requestId = dvmJdwpNextEventSerial(state);
1538    expandBufAdd4BE(pReply, requestId);
1539
1540    pEvent->requestId = requestId;
1541
1542    ALOGV("    --> event requestId=%#x", requestId);
1543
1544    /* add it to the list */
1545    JdwpError err = dvmJdwpRegisterEvent(state, pEvent);
1546    if (err != ERR_NONE) {
1547        /* registration failed, probably because event is bogus */
1548        dvmJdwpEventFree(pEvent);
1549        ALOGW("WARNING: event request rejected");
1550    }
1551    return err;
1552}
1553
1554/*
1555 * Clear an event.  Failure to find an event with a matching ID is a no-op
1556 * and does not return an error.
1557 */
1558static JdwpError handleER_Clear(JdwpState* state,
1559    const u1* buf, int dataLen, ExpandBuf* pReply)
1560{
1561    u1 eventKind;
1562    eventKind = read1(&buf);
1563    u4 requestId = read4BE(&buf);
1564
1565    ALOGV("  Req to clear eventKind=%d requestId=%#x", eventKind, requestId);
1566
1567    dvmJdwpUnregisterEventById(state, requestId);
1568
1569    return ERR_NONE;
1570}
1571
1572/*
1573 * Return the values of arguments and local variables.
1574 */
1575static JdwpError handleSF_GetValues(JdwpState* state,
1576    const u1* buf, int dataLen, ExpandBuf* pReply)
1577{
1578    ObjectId threadId = dvmReadObjectId(&buf);
1579    FrameId frameId = dvmReadFrameId(&buf);
1580    u4 slots = read4BE(&buf);
1581
1582    ALOGV("  Req for %d slots in threadId=%llx frameId=%llx",
1583        slots, threadId, frameId);
1584
1585    expandBufAdd4BE(pReply, slots);     /* "int values" */
1586    for (u4 i = 0; i < slots; i++) {
1587        u4 slot = read4BE(&buf);
1588        u1 reqSigByte = read1(&buf);
1589
1590        ALOGV("    --> slot %d '%c'", slot, reqSigByte);
1591
1592        int width = dvmDbgGetTagWidth(reqSigByte);
1593        u1* ptr = expandBufAddSpace(pReply, width+1);
1594        dvmDbgGetLocalValue(threadId, frameId, slot, reqSigByte, ptr, width);
1595    }
1596
1597    return ERR_NONE;
1598}
1599
1600/*
1601 * Set the values of arguments and local variables.
1602 */
1603static JdwpError handleSF_SetValues(JdwpState* state,
1604    const u1* buf, int dataLen, ExpandBuf* pReply)
1605{
1606    ObjectId threadId = dvmReadObjectId(&buf);
1607    FrameId frameId = dvmReadFrameId(&buf);
1608    u4 slots = read4BE(&buf);
1609
1610    ALOGV("  Req to set %d slots in threadId=%llx frameId=%llx",
1611        slots, threadId, frameId);
1612
1613    for (u4 i = 0; i < slots; i++) {
1614        u4 slot = read4BE(&buf);
1615        u1 sigByte = read1(&buf);
1616        int width = dvmDbgGetTagWidth(sigByte);
1617        u8 value = jdwpReadValue(&buf, width);
1618
1619        ALOGV("    --> slot %d '%c' %llx", slot, sigByte, value);
1620        dvmDbgSetLocalValue(threadId, frameId, slot, sigByte, value, width);
1621    }
1622
1623    return ERR_NONE;
1624}
1625
1626/*
1627 * Returns the value of "this" for the specified frame.
1628 */
1629static JdwpError handleSF_ThisObject(JdwpState* state,
1630    const u1* buf, int dataLen, ExpandBuf* pReply)
1631{
1632    ObjectId threadId = dvmReadObjectId(&buf);
1633    FrameId frameId = dvmReadFrameId(&buf);
1634
1635    ObjectId objectId;
1636    if (!dvmDbgGetThisObject(threadId, frameId, &objectId))
1637        return ERR_INVALID_FRAMEID;
1638
1639    u1 objectTag = dvmDbgGetObjectTag(objectId);
1640    ALOGV("  Req for 'this' in thread=%llx frame=%llx --> %llx %s '%c'",
1641        threadId, frameId, objectId, dvmDbgGetObjectTypeName(objectId),
1642        (char)objectTag);
1643
1644    expandBufAdd1(pReply, objectTag);
1645    expandBufAddObjectId(pReply, objectId);
1646
1647    return ERR_NONE;
1648}
1649
1650/*
1651 * Return the reference type reflected by this class object.
1652 *
1653 * This appears to be required because ReferenceTypeId values are NEVER
1654 * reused, whereas ClassIds can be recycled like any other object.  (Either
1655 * that, or I have no idea what this is for.)
1656 */
1657static JdwpError handleCOR_ReflectedType(JdwpState* state,
1658    const u1* buf, int dataLen, ExpandBuf* pReply)
1659{
1660    RefTypeId classObjectId = dvmReadRefTypeId(&buf);
1661
1662    ALOGV("  Req for refTypeId for class=%llx (%s)",
1663        classObjectId, dvmDbgGetClassDescriptor(classObjectId));
1664
1665    /* just hand the type back to them */
1666    if (dvmDbgIsInterface(classObjectId))
1667        expandBufAdd1(pReply, TT_INTERFACE);
1668    else
1669        expandBufAdd1(pReply, TT_CLASS);
1670    expandBufAddRefTypeId(pReply, classObjectId);
1671
1672    return ERR_NONE;
1673}
1674
1675/*
1676 * Handle a DDM packet with a single chunk in it.
1677 */
1678static JdwpError handleDDM_Chunk(JdwpState* state,
1679    const u1* buf, int dataLen, ExpandBuf* pReply)
1680{
1681    u1* replyBuf = NULL;
1682    int replyLen = -1;
1683
1684    ALOGV("  Handling DDM packet (%.4s)", buf);
1685
1686    /*
1687     * On first DDM packet, notify all handlers that DDM is running.
1688     */
1689    if (!state->ddmActive) {
1690        state->ddmActive = true;
1691        dvmDbgDdmConnected();
1692    }
1693
1694    /*
1695     * If they want to send something back, we copy it into the buffer.
1696     * A no-copy approach would be nicer.
1697     *
1698     * TODO: consider altering the JDWP stuff to hold the packet header
1699     * in a separate buffer.  That would allow us to writev() DDM traffic
1700     * instead of copying it into the expanding buffer.  The reduction in
1701     * heap requirements is probably more valuable than the efficiency.
1702     */
1703    if (dvmDbgDdmHandlePacket(buf, dataLen, &replyBuf, &replyLen)) {
1704        assert(replyLen > 0 && replyLen < 1*1024*1024);
1705        memcpy(expandBufAddSpace(pReply, replyLen), replyBuf, replyLen);
1706        free(replyBuf);
1707    }
1708    return ERR_NONE;
1709}
1710
1711/*
1712 * Handler map decl.
1713 */
1714typedef JdwpError (*JdwpRequestHandler)(JdwpState* state,
1715    const u1* buf, int dataLen, ExpandBuf* reply);
1716
1717struct JdwpHandlerMap {
1718    u1  cmdSet;
1719    u1  cmd;
1720    JdwpRequestHandler  func;
1721    const char* descr;
1722};
1723
1724/*
1725 * Map commands to functions.
1726 *
1727 * Command sets 0-63 are incoming requests, 64-127 are outbound requests,
1728 * and 128-256 are vendor-defined.
1729 */
1730static const JdwpHandlerMap gHandlerMap[] = {
1731    /* VirtualMachine command set (1) */
1732    { 1,    1,  handleVM_Version,       "VirtualMachine.Version" },
1733    { 1,    2,  handleVM_ClassesBySignature,
1734                                        "VirtualMachine.ClassesBySignature" },
1735    //1,    3,  VirtualMachine.AllClasses
1736    { 1,    4,  handleVM_AllThreads,    "VirtualMachine.AllThreads" },
1737    { 1,    5,  handleVM_TopLevelThreadGroups,
1738                                        "VirtualMachine.TopLevelThreadGroups" },
1739    { 1,    6,  handleVM_Dispose,       "VirtualMachine.Dispose" },
1740    { 1,    7,  handleVM_IDSizes,       "VirtualMachine.IDSizes" },
1741    { 1,    8,  handleVM_Suspend,       "VirtualMachine.Suspend" },
1742    { 1,    9,  handleVM_Resume,        "VirtualMachine.Resume" },
1743    { 1,    10, handleVM_Exit,          "VirtualMachine.Exit" },
1744    { 1,    11, handleVM_CreateString,  "VirtualMachine.CreateString" },
1745    { 1,    12, handleVM_Capabilities,  "VirtualMachine.Capabilities" },
1746    { 1,    13, handleVM_ClassPaths,    "VirtualMachine.ClassPaths" },
1747    { 1,    14, HandleVM_DisposeObjects, "VirtualMachine.DisposeObjects" },
1748    //1,    15, HoldEvents
1749    //1,    16, ReleaseEvents
1750    { 1,    17, handleVM_CapabilitiesNew,
1751                                        "VirtualMachine.CapabilitiesNew" },
1752    //1,    18, RedefineClasses
1753    //1,    19, SetDefaultStratum
1754    { 1,    20, handleVM_AllClassesWithGeneric,
1755                                        "VirtualMachine.AllClassesWithGeneric"},
1756    //1,    21, InstanceCounts
1757
1758    /* ReferenceType command set (2) */
1759    { 2,    1,  handleRT_Signature,     "ReferenceType.Signature" },
1760    { 2,    2,  handleRT_ClassLoader,   "ReferenceType.ClassLoader" },
1761    { 2,    3,  handleRT_Modifiers,     "ReferenceType.Modifiers" },
1762    //2,    4,  Fields
1763    //2,    5,  Methods
1764    { 2,    6,  handleRT_GetValues,     "ReferenceType.GetValues" },
1765    { 2,    7,  handleRT_SourceFile,    "ReferenceType.SourceFile" },
1766    //2,    8,  NestedTypes
1767    { 2,    9,  handleRT_Status,        "ReferenceType.Status" },
1768    { 2,    10, handleRT_Interfaces,    "ReferenceType.Interfaces" },
1769    { 2,    11, handleRT_ClassObject,   "ReferenceType.ClassObject" },
1770    { 2,    12, handleRT_SourceDebugExtension,
1771                                        "ReferenceType.SourceDebugExtension" },
1772    { 2,    13, handleRT_SignatureWithGeneric,
1773                                        "ReferenceType.SignatureWithGeneric" },
1774    { 2,    14, handleRT_FieldsWithGeneric,
1775                                        "ReferenceType.FieldsWithGeneric" },
1776    { 2,    15, handleRT_MethodsWithGeneric,
1777                                        "ReferenceType.MethodsWithGeneric" },
1778    //2,    16, Instances
1779    //2,    17, ClassFileVersion
1780    //2,    18, ConstantPool
1781
1782    /* ClassType command set (3) */
1783    { 3,    1,  handleCT_Superclass,    "ClassType.Superclass" },
1784    { 3,    2,  handleCT_SetValues,     "ClassType.SetValues" },
1785    { 3,    3,  handleCT_InvokeMethod,  "ClassType.InvokeMethod" },
1786    { 3,    4,  handleCT_NewInstance,   "ClassType.NewInstance" },
1787
1788    /* ArrayType command set (4) */
1789    { 4,    1,  handleAT_newInstance,   "ArrayType.NewInstance" },
1790
1791    /* InterfaceType command set (5) */
1792
1793    /* Method command set (6) */
1794    { 6,    1,  handleM_LineTable,      "Method.LineTable" },
1795    //6,    2,  VariableTable
1796    //6,    3,  Bytecodes
1797    //6,    4,  IsObsolete
1798    { 6,    5,  handleM_VariableTableWithGeneric,
1799                                        "Method.VariableTableWithGeneric" },
1800
1801    /* Field command set (8) */
1802
1803    /* ObjectReference command set (9) */
1804    { 9,    1,  handleOR_ReferenceType, "ObjectReference.ReferenceType" },
1805    { 9,    2,  handleOR_GetValues,     "ObjectReference.GetValues" },
1806    { 9,    3,  handleOR_SetValues,     "ObjectReference.SetValues" },
1807    //9,    4,  (not defined)
1808    //9,    5,  MonitorInfo
1809    { 9,    6,  handleOR_InvokeMethod,  "ObjectReference.InvokeMethod" },
1810    { 9,    7,  handleOR_DisableCollection,
1811                                        "ObjectReference.DisableCollection" },
1812    { 9,    8,  handleOR_EnableCollection,
1813                                        "ObjectReference.EnableCollection" },
1814    { 9,    9,  handleOR_IsCollected,   "ObjectReference.IsCollected" },
1815    //9,    10, ReferringObjects
1816
1817    /* StringReference command set (10) */
1818    { 10,   1,  handleSR_Value,         "StringReference.Value" },
1819
1820    /* ThreadReference command set (11) */
1821    { 11,   1,  handleTR_Name,          "ThreadReference.Name" },
1822    { 11,   2,  handleTR_Suspend,       "ThreadReference.Suspend" },
1823    { 11,   3,  handleTR_Resume,        "ThreadReference.Resume" },
1824    { 11,   4,  handleTR_Status,        "ThreadReference.Status" },
1825    { 11,   5,  handleTR_ThreadGroup,   "ThreadReference.ThreadGroup" },
1826    { 11,   6,  handleTR_Frames,        "ThreadReference.Frames" },
1827    { 11,   7,  handleTR_FrameCount,    "ThreadReference.FrameCount" },
1828    //11,   8,  OwnedMonitors
1829    { 11,   9,  handleTR_CurrentContendedMonitor,
1830                                    "ThreadReference.CurrentContendedMonitor" },
1831    //11,   10, Stop
1832    //11,   11, Interrupt
1833    { 11,   12, handleTR_SuspendCount,  "ThreadReference.SuspendCount" },
1834    //11,   13, OwnedMonitorsStackDepthInfo
1835    //11,   14, ForceEarlyReturn
1836
1837    /* ThreadGroupReference command set (12) */
1838    { 12,   1,  handleTGR_Name,         "ThreadGroupReference.Name" },
1839    { 12,   2,  handleTGR_Parent,       "ThreadGroupReference.Parent" },
1840    { 12,   3,  handleTGR_Children,     "ThreadGroupReference.Children" },
1841
1842    /* ArrayReference command set (13) */
1843    { 13,   1,  handleAR_Length,        "ArrayReference.Length" },
1844    { 13,   2,  handleAR_GetValues,     "ArrayReference.GetValues" },
1845    { 13,   3,  handleAR_SetValues,     "ArrayReference.SetValues" },
1846
1847    /* ClassLoaderReference command set (14) */
1848    { 14,   1,  handleCLR_VisibleClasses,
1849                                        "ClassLoaderReference.VisibleClasses" },
1850
1851    /* EventRequest command set (15) */
1852    { 15,   1,  handleER_Set,           "EventRequest.Set" },
1853    { 15,   2,  handleER_Clear,         "EventRequest.Clear" },
1854    //15,   3,  ClearAllBreakpoints
1855
1856    /* StackFrame command set (16) */
1857    { 16,   1,  handleSF_GetValues,     "StackFrame.GetValues" },
1858    { 16,   2,  handleSF_SetValues,     "StackFrame.SetValues" },
1859    { 16,   3,  handleSF_ThisObject,    "StackFrame.ThisObject" },
1860    //16,   4,  PopFrames
1861
1862    /* ClassObjectReference command set (17) */
1863    { 17,   1,  handleCOR_ReflectedType,"ClassObjectReference.ReflectedType" },
1864
1865    /* Event command set (64) */
1866    //64,  100, Composite   <-- sent from VM to debugger, never received by VM
1867
1868    { 199,  1,  handleDDM_Chunk,        "DDM.Chunk" },
1869};
1870
1871
1872/*
1873 * Process a request from the debugger.
1874 *
1875 * On entry, the JDWP thread is in VMWAIT.
1876 */
1877void dvmJdwpProcessRequest(JdwpState* state, const JdwpReqHeader* pHeader,
1878    const u1* buf, int dataLen, ExpandBuf* pReply)
1879{
1880    JdwpError result = ERR_NONE;
1881    int i, respLen;
1882
1883    if (pHeader->cmdSet != kJDWPDdmCmdSet) {
1884        /*
1885         * Activity from a debugger, not merely ddms.  Mark us as having an
1886         * active debugger session, and zero out the last-activity timestamp
1887         * so waitForDebugger() doesn't return if we stall for a bit here.
1888         */
1889        dvmDbgActive();
1890        dvmQuasiAtomicSwap64(0, &state->lastActivityWhen);
1891    }
1892
1893    /*
1894     * If a debugger event has fired in another thread, wait until the
1895     * initiating thread has suspended itself before processing messages
1896     * from the debugger.  Otherwise we (the JDWP thread) could be told to
1897     * resume the thread before it has suspended.
1898     *
1899     * We call with an argument of zero to wait for the current event
1900     * thread to finish, and then clear the block.  Depending on the thread
1901     * suspend policy, this may allow events in other threads to fire,
1902     * but those events have no bearing on what the debugger has sent us
1903     * in the current request.
1904     *
1905     * Note that we MUST clear the event token before waking the event
1906     * thread up, or risk waiting for the thread to suspend after we've
1907     * told it to resume.
1908     */
1909    dvmJdwpSetWaitForEventThread(state, 0);
1910
1911    /*
1912     * Tell the VM that we're running and shouldn't be interrupted by GC.
1913     * Do this after anything that can stall indefinitely.
1914     */
1915    dvmDbgThreadRunning();
1916
1917    expandBufAddSpace(pReply, kJDWPHeaderLen);
1918
1919    for (i = 0; i < (int) NELEM(gHandlerMap); i++) {
1920        if (gHandlerMap[i].cmdSet == pHeader->cmdSet &&
1921            gHandlerMap[i].cmd == pHeader->cmd)
1922        {
1923            ALOGV("REQ: %s (cmd=%d/%d dataLen=%d id=0x%06x)",
1924                gHandlerMap[i].descr, pHeader->cmdSet, pHeader->cmd,
1925                dataLen, pHeader->id);
1926            result = (*gHandlerMap[i].func)(state, buf, dataLen, pReply);
1927            break;
1928        }
1929    }
1930    if (i == NELEM(gHandlerMap)) {
1931        ALOGE("REQ: UNSUPPORTED (cmd=%d/%d dataLen=%d id=0x%06x)",
1932            pHeader->cmdSet, pHeader->cmd, dataLen, pHeader->id);
1933        if (dataLen > 0)
1934            dvmPrintHexDumpDbg(buf, dataLen, LOG_TAG);
1935        assert(!"command not implemented");      // make it *really* obvious
1936        result = ERR_NOT_IMPLEMENTED;
1937    }
1938
1939    /*
1940     * Set up the reply header.
1941     *
1942     * If we encountered an error, only send the header back.
1943     */
1944    u1* replyBuf = expandBufGetBuffer(pReply);
1945    set4BE(replyBuf + 4, pHeader->id);
1946    set1(replyBuf + 8, kJDWPFlagReply);
1947    set2BE(replyBuf + 9, result);
1948    if (result == ERR_NONE)
1949        set4BE(replyBuf + 0, expandBufGetLength(pReply));
1950    else
1951        set4BE(replyBuf + 0, kJDWPHeaderLen);
1952
1953    respLen = expandBufGetLength(pReply) - kJDWPHeaderLen;
1954    IF_ALOG(LOG_VERBOSE, LOG_TAG) {
1955        ALOGV("reply: dataLen=%d err=%s(%d)%s", respLen,
1956            dvmJdwpErrorStr(result), result,
1957            result != ERR_NONE ? " **FAILED**" : "");
1958        if (respLen > 0)
1959            dvmPrintHexDumpDbg(expandBufGetBuffer(pReply) + kJDWPHeaderLen,
1960                respLen, LOG_TAG);
1961    }
1962
1963    /*
1964     * Update last-activity timestamp.  We really only need this during
1965     * the initial setup.  Only update if this is a non-DDMS packet.
1966     */
1967    if (pHeader->cmdSet != kJDWPDdmCmdSet) {
1968        dvmQuasiAtomicSwap64(dvmJdwpGetNowMsec(), &state->lastActivityWhen);
1969    }
1970
1971    /* tell the VM that GC is okay again */
1972    dvmDbgThreadWaiting();
1973}
1974