예제 #1
0
/*
 * Find the return type in the signature, and convert it to a class
 * object.  For primitive types we use a boxed class, for reference types
 * we do a name lookup.
 *
 * On failure, we return NULL with an exception raised.
 */
ClassObject* dvmGetBoxedReturnType(const Method* meth)
{
    const char* sig = dexProtoGetReturnType(&meth->prototype);

    switch (*sig) {
    case 'Z':
    case 'C':
    case 'F':
    case 'D':
    case 'B':
    case 'S':
    case 'I':
    case 'J':
    case 'V':
        return dvmFindPrimitiveClass(*sig);
    case '[':
    case 'L':
        return dvmFindClass(sig, meth->clazz->classLoader);
    default: {
        /* should not have passed verification */
        char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
        ALOGE("Bad return type in signature '%s'", desc);
        free(desc);
        dvmThrowInternalError(NULL);
        return NULL;
    }
    }
}
예제 #2
0
static bool performanceTest()
{
    static const int kTableMax = 100;
    IndirectRefTable irt;
    IndirectRef manyRefs[kTableMax];
    ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL);
    Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
    const u4 cookie = IRT_FIRST_SEGMENT;
    const int kLoops = 100000;
    Stopwatch stopwatch;

    DBUG_MSG("+++ START performance\n");

    if (!irt.init(kTableMax, kTableMax, kIndirectKindGlobal)) {
        return false;
    }

    stopwatch.reset();
    for (int loop = 0; loop < kLoops; loop++) {
        for (int i = 0; i < kTableMax; i++) {
            manyRefs[i] = irt.add(cookie, obj0);
        }
        for (int i = 0; i < kTableMax; i++) {
            irt.remove(cookie, manyRefs[i]);
        }
    }
    DBUG_MSG("Add/remove %d objects FIFO order, %d iterations, %0.3fms / iteration",
            kTableMax, kLoops, stopwatch.elapsedSeconds() * 1000 / kLoops);

    stopwatch.reset();
    for (int loop = 0; loop < kLoops; loop++) {
        for (int i = 0; i < kTableMax; i++) {
            manyRefs[i] = irt.add(cookie, obj0);
        }
        for (int i = kTableMax; i-- > 0; ) {
            irt.remove(cookie, manyRefs[i]);
        }
    }
    DBUG_MSG("Add/remove %d objects LIFO order, %d iterations, %0.3fms / iteration",
            kTableMax, kLoops, stopwatch.elapsedSeconds() * 1000  / kLoops);

    for (int i = 0; i < kTableMax; i++) {
        manyRefs[i] = irt.add(cookie, obj0);
    }
    stopwatch.reset();
    for (int loop = 0; loop < kLoops; loop++) {
        for (int i = 0; i < kTableMax; i++) {
            irt.get(manyRefs[i]);
        }
    }
    DBUG_MSG("Get %d objects, %d iterations, %0.3fms / iteration",
            kTableMax, kLoops, stopwatch.elapsedSeconds() * 1000  / kLoops);
    for (int i = kTableMax; i-- > 0; ) {
        irt.remove(cookie, manyRefs[i]);
    }

    irt.destroy();
    return true;
}
예제 #3
0
/*
 * Find a class by name, initializing it if requested.
 */
ClassObject* dvmFindClassByName(StringObject* nameObj, Object* loader,
    bool doInit)
{
    ClassObject* clazz = NULL;
    char* name = NULL;
    char* descriptor = NULL;

    if (nameObj == NULL) {
        dvmThrowException("Ljava/lang/NullPointerException;", NULL);
        goto bail;
    }
    name = dvmCreateCstrFromString(nameObj);

    /*
     * We need to validate and convert the name (from x.y.z to x/y/z).  This
     * is especially handy for array types, since we want to avoid
     * auto-generating bogus array classes.
     */
    if (!validateClassName(name)) {
        LOGW("dvmFindClassByName rejecting '%s'\n", name);
        dvmThrowException("Ljava/lang/ClassNotFoundException;", name);
        goto bail;
    }

    descriptor = dvmDotToDescriptor(name);
    if (descriptor == NULL) {
        goto bail;
    }

    if (doInit)
        clazz = dvmFindClass(descriptor, loader);
    else
        clazz = dvmFindClassNoInit(descriptor, loader);

    if (clazz == NULL) {
        LOGVV("FAIL: load %s (%d)\n", descriptor, doInit);
        Thread* self = dvmThreadSelf();
        Object* oldExcep = dvmGetException(self);
        dvmAddTrackedAlloc(oldExcep, self);     /* don't let this be GCed */
        dvmClearException(self);
        dvmThrowChainedException("Ljava/lang/ClassNotFoundException;",
            name, oldExcep);
        dvmReleaseTrackedAlloc(oldExcep, self);
    } else {
        LOGVV("GOOD: load %s (%d) --> %p ldr=%p\n",
            descriptor, doInit, clazz, clazz->classLoader);
    }

bail:
    free(name);
    free(descriptor);
    return clazz;
}
예제 #4
0
/*
 * Broadcast an event to all handlers.
 */
static void broadcast(int event)
{
    ClassObject* ddmServerClass;
    Method* bcast;

    ddmServerClass =
        dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/DdmServer;", NULL);
    if (ddmServerClass == NULL) {
        LOGW("Unable to find org.apache.harmony.dalvik.ddmc.DdmServer\n");
        goto bail;
    }
    bcast = dvmFindDirectMethodByDescriptor(ddmServerClass, "broadcast", "(I)V");
    if (bcast == NULL) {
        LOGW("Unable to find DdmServer.broadcast\n");
        goto bail;
    }

    Thread* self = dvmThreadSelf();

    if (self->status != THREAD_RUNNING) {
        LOGE("ERROR: DDM broadcast with thread status=%d\n", self->status);
        /* try anyway? */
    }

    JValue unused;
    dvmCallMethod(self, bcast, NULL, &unused, event);
    if (dvmCheckException(self)) {
        LOGI("Exception thrown by broadcast(%d)\n", event);
        dvmLogExceptionStackTrace();
        dvmClearException(self);
        goto bail;
    }

bail:
    ;
}
예제 #5
0
/*
 * Basic add/get/delete tests in an unsegmented table.
 */
static bool basicTest()
{
    static const int kTableMax = 20;
    IndirectRefTable irt;
    IndirectRef iref0, iref1, iref2, iref3;
    IndirectRef manyRefs[kTableMax];
    ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL);
    Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
    Object* obj1 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
    Object* obj2 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
    Object* obj3 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
    const u4 cookie = IRT_FIRST_SEGMENT;
    bool result = false;

    if (!irt.init(kTableMax/2, kTableMax, kIndirectKindGlobal)) {
        return false;
    }

    iref0 = (IndirectRef) 0x11110;
    if (irt.remove(cookie, iref0)) {
        ALOGE("unexpectedly successful removal");
        goto bail;
    }

    /*
     * Add three, check, remove in the order in which they were added.
     */
    DBUG_MSG("+++ START fifo\n");
    iref0 = irt.add(cookie, obj0);
    iref1 = irt.add(cookie, obj1);
    iref2 = irt.add(cookie, obj2);
    if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
        ALOGE("trivial add1 failed");
        goto bail;
    }

    if (irt.get(iref0) != obj0 ||
            irt.get(iref1) != obj1 ||
            irt.get(iref2) != obj2) {
        ALOGE("objects don't match expected values %p %p %p vs. %p %p %p",
                irt.get(iref0), irt.get(iref1), irt.get(iref2),
                obj0, obj1, obj2);
        goto bail;
    } else {
        DBUG_MSG("+++ obj1=%p --> iref1=%p\n", obj1, iref1);
    }

    if (!irt.remove(cookie, iref0) ||
            !irt.remove(cookie, iref1) ||
            !irt.remove(cookie, iref2))
    {
        ALOGE("fifo deletion failed");
        goto bail;
    }

    /* table should be empty now */
    if (irt.capacity() != 0) {
        ALOGE("fifo del not empty");
        goto bail;
    }

    /* get invalid entry (off the end of the list) */
    if (irt.get(iref0) != kInvalidIndirectRefObject) {
        ALOGE("stale entry get succeeded unexpectedly");
        goto bail;
    }

    /*
     * Add three, remove in the opposite order.
     */
    DBUG_MSG("+++ START lifo\n");
    iref0 = irt.add(cookie, obj0);
    iref1 = irt.add(cookie, obj1);
    iref2 = irt.add(cookie, obj2);
    if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
        ALOGE("trivial add2 failed");
        goto bail;
    }

    if (!irt.remove(cookie, iref2) ||
            !irt.remove(cookie, iref1) ||
            !irt.remove(cookie, iref0))
    {
        ALOGE("lifo deletion failed");
        goto bail;
    }

    /* table should be empty now */
    if (irt.capacity() != 0) {
        ALOGE("lifo del not empty");
        goto bail;
    }

    /*
     * Add three, remove middle / middle / bottom / top.  (Second attempt
     * to remove middle should fail.)
     */
    DBUG_MSG("+++ START unorder\n");
    iref0 = irt.add(cookie, obj0);
    iref1 = irt.add(cookie, obj1);
    iref2 = irt.add(cookie, obj2);
    if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
        ALOGE("trivial add3 failed");
        goto bail;
    }

    if (irt.capacity() != 3) {
        ALOGE("expected 3 entries, found %d", irt.capacity());
        goto bail;
    }

    if (!irt.remove(cookie, iref1) || irt.remove(cookie, iref1)) {
        ALOGE("unorder deletion1 failed");
        goto bail;
    }

    /* get invalid entry (from hole) */
    if (irt.get(iref1) != kInvalidIndirectRefObject) {
        ALOGE("hole get succeeded unexpectedly");
        goto bail;
    }

    if (!irt.remove(cookie, iref2) || !irt.remove(cookie, iref0)) {
        ALOGE("unorder deletion2 failed");
        goto bail;
    }

    /* table should be empty now */
    if (irt.capacity() != 0) {
        ALOGE("unorder del not empty");
        goto bail;
    }

    /*
     * Add four entries.  Remove #1, add new entry, verify that table size
     * is still 4 (i.e. holes are getting filled).  Remove #1 and #3, verify
     * that we delete one and don't hole-compact the other.
     */
    DBUG_MSG("+++ START hole fill\n");
    iref0 = irt.add(cookie, obj0);
    iref1 = irt.add(cookie, obj1);
    iref2 = irt.add(cookie, obj2);
    iref3 = irt.add(cookie, obj3);
    if (iref0 == NULL || iref1 == NULL || iref2 == NULL || iref3 == NULL) {
        ALOGE("trivial add4 failed");
        goto bail;
    }
    if (!irt.remove(cookie, iref1)) {
        ALOGE("remove 1 of 4 failed");
        goto bail;
    }
    iref1 = irt.add(cookie, obj1);
    if (irt.capacity() != 4) {
        ALOGE("hole not filled");
        goto bail;
    }
    if (!irt.remove(cookie, iref1) || !irt.remove(cookie, iref3)) {
        ALOGE("remove 1/3 failed");
        goto bail;
    }
    if (irt.capacity() != 3) {
        ALOGE("should be 3 after two deletions");
        goto bail;
    }
    if (!irt.remove(cookie, iref2) || !irt.remove(cookie, iref0)) {
        ALOGE("remove 2/0 failed");
        goto bail;
    }
    if (irt.capacity() != 0) {
        ALOGE("not empty after split remove");
        goto bail;
    }

    /*
     * Add an entry, remove it, add a new entry, and try to use the original
     * iref.  They have the same slot number but are for different objects.
     * With the extended checks in place, this should fail.
     */
    DBUG_MSG("+++ START switched\n");
    iref0 = irt.add(cookie, obj0);
    irt.remove(cookie, iref0);
    iref1 = irt.add(cookie, obj1);
    if (irt.remove(cookie, iref0)) {
        ALOGE("mismatched del succeeded (%p vs %p)", iref0, iref1);
        goto bail;
    }
    if (!irt.remove(cookie, iref1)) {
        ALOGE("switched del failed");
        goto bail;
    }
    if (irt.capacity() != 0) {
        ALOGE("switching del not empty");
        goto bail;
    }

    /*
     * Same as above, but with the same object.  A more rigorous checker
     * (e.g. with slot serialization) will catch this.
     */
    DBUG_MSG("+++ START switched same object\n");
    iref0 = irt.add(cookie, obj0);
    irt.remove(cookie, iref0);
    iref1 = irt.add(cookie, obj0);
    if (iref0 != iref1) {
        /* try 0, should not work */
        if (irt.remove(cookie, iref0)) {
            ALOGE("temporal del succeeded (%p vs %p)", iref0, iref1);
            goto bail;
        }
    }
    if (!irt.remove(cookie, iref1)) {
        ALOGE("temporal cleanup failed");
        goto bail;
    }
    if (irt.capacity() != 0) {
        ALOGE("temporal del not empty");
        goto bail;
    }

    DBUG_MSG("+++ START null lookup\n");
    if (irt.get(NULL) != kInvalidIndirectRefObject) {
        ALOGE("null lookup succeeded");
        goto bail;
    }

    DBUG_MSG("+++ START stale lookup\n");
    iref0 = irt.add(cookie, obj0);
    irt.remove(cookie, iref0);
    if (irt.get(iref0) != kInvalidIndirectRefObject) {
        ALOGE("stale lookup succeeded");
        goto bail;
    }

    /*
     * Test table overflow.
     */
    DBUG_MSG("+++ START overflow\n");
    int i;
    for (i = 0; i < kTableMax; i++) {
        manyRefs[i] = irt.add(cookie, obj0);
        if (manyRefs[i] == NULL) {
            ALOGE("Failed adding %d of %d", i, kTableMax);
            goto bail;
        }
    }
    if (irt.add(cookie, obj0) != NULL) {
        ALOGE("Table overflow succeeded");
        goto bail;
    }
    if (irt.capacity() != (size_t)kTableMax) {
        ALOGE("Expected %d entries, found %d", kTableMax, irt.capacity());
        goto bail;
    }
    irt.dump("table with 20 entries, all filled");
    for (i = 0; i < kTableMax-1; i++) {
        if (!irt.remove(cookie, manyRefs[i])) {
            ALOGE("multi-remove failed at %d", i);
            goto bail;
        }
    }
    irt.dump("table with 20 entries, 19 of them holes");
    /* because of removal order, should have 20 entries, 19 of them holes */
    if (irt.capacity() != (size_t)kTableMax) {
        ALOGE("Expected %d entries (with holes), found %d",
                kTableMax, irt.capacity());
        goto bail;
    }
    if (!irt.remove(cookie, manyRefs[kTableMax-1])) {
        ALOGE("multi-remove final failed");
        goto bail;
    }
    if (irt.capacity() != 0) {
        ALOGE("multi-del not empty");
        goto bail;
    }

    /* Done */
    DBUG_MSG("+++ basic test complete\n");
    result = true;

bail:
    irt.destroy();
    return result;
}
/*
 * Test operations on a segmented table.
 */
static bool segmentTest(void)
{
    static const int kTableMax = 20;
    IndirectRefTable irt;
    IndirectRef iref0, iref1, iref2, iref3;
    ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL);
    Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
    Object* obj1 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
    Object* obj2 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
    Object* obj3 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
    u4 cookie;
    u4 segmentState[4];
    bool result = false;

    if (!dvmInitIndirectRefTable(&irt, kTableMax, kTableMax,
            kIndirectKindLocal))
    {
        return false;
    }
    cookie = segmentState[0] = IRT_FIRST_SEGMENT;
    DBUG_MSG("+++ objs %p %p %p %p\n", obj0, obj1, obj2, obj3);

    /*
     * Push two, create new segment, push two more, try to get all four,
     * try to delete all 4.  All four should be accessible, but only the
     * last two should be deletable.
     */
    DBUG_MSG("+++ START basic segment\n");
    iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
    cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt);
    DBUG_MSG("+++ pushed, cookie is 0x%08x\n", cookie);
    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
    iref3 = dvmAddToIndirectRefTable(&irt, cookie, obj3);

    if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0) ||
        dvmRemoveFromIndirectRefTable(&irt, cookie, iref1))
    {
        LOGE("removed values from earlier segment\n");
        goto bail;
    }
    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) ||
        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref3))
    {
        LOGE("unable to remove values from current segment\n");
        goto bail;
    }
    if (dvmIndirectRefTableEntries(&irt) != 2) {
        LOGE("wrong total entries\n");
        goto bail;
    }
    dvmPopIndirectRefTableSegment(&irt, segmentState[1]);
    cookie = segmentState[0];
    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref0) ||
        !dvmRemoveFromIndirectRefTable(&irt, cookie, iref1))
    {
        LOGE("unable to remove values from first segment\n");
        goto bail;
    }
    if (dvmIndirectRefTableEntries(&irt) != 0) {
        LOGE("basic push/pop not empty\n");
        goto bail;
    }

    /*
     * Push two, delete first, segment, push two more, pop segment, verify
     * the last two are no longer present and hole count is right.  The
     * adds after the segment pop should not be filling in the hole.
     */
    DBUG_MSG("+++ START segment pop\n");
    iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0);
    iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1);
    dvmRemoveFromIndirectRefTable(&irt, cookie, iref0);
    cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt);
    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);
    iref3 = dvmAddToIndirectRefTable(&irt, cookie, obj3);
    dvmPopIndirectRefTableSegment(&irt, segmentState[1]);
    cookie = segmentState[0];
    if (dvmIndirectRefTableEntries(&irt) != 2) {
        LOGE("wrong total entries after pop\n");
        goto bail;
    }
    dvmRemoveFromIndirectRefTable(&irt, cookie, iref1);
    if (dvmIndirectRefTableEntries(&irt) != 0) {
        LOGE("not back to zero after pop + del\n");
        goto bail;
    }

    /*
     * Multiple segments, some empty.
     */
    DBUG_MSG("+++ START multiseg\n");
    iref0 = dvmAppendToIndirectRefTable(&irt, cookie, obj0);
    iref1 = dvmAppendToIndirectRefTable(&irt, cookie, obj1);
    cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt);
    cookie = segmentState[2] = dvmPushIndirectRefTableSegment(&irt);
    iref3 = dvmAppendToIndirectRefTable(&irt, cookie, obj3);
    iref2 = dvmAppendToIndirectRefTable(&irt, cookie, obj2);
    dvmRemoveFromIndirectRefTable(&irt, cookie, iref3);
    cookie = segmentState[3] = dvmPushIndirectRefTableSegment(&irt);
    iref3 = dvmAppendToIndirectRefTable(&irt, cookie, obj3);

    if (dvmGetFromIndirectRefTable(&irt, iref0) != obj0 ||
        dvmGetFromIndirectRefTable(&irt, iref1) != obj1 ||
        dvmGetFromIndirectRefTable(&irt, iref2) != obj2 ||
        dvmGetFromIndirectRefTable(&irt, iref3) != obj3)
    {
        LOGE("Unable to retrieve all multiseg objects\n");
        goto bail;
    }

    dvmDumpIndirectRefTable(&irt, "test");

    //int i;
    //for (i = 0; i < sizeof(segmentState) / sizeof(segmentState[0]); i++) {
    //    DBUG_MSG("+++  segment %d = 0x%08x\n", i, segmentState[i]);
    //}

    dvmRemoveFromIndirectRefTable(&irt, cookie, iref3);
    if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref2)) {
        LOGE("multiseg del2 worked\n");
        goto bail;
    }
    dvmPopIndirectRefTableSegment(&irt, segmentState[3]);
    cookie = segmentState[2];
    if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2)) {
        LOGE("multiseg del2b failed (cookie=0x%08x ref=%p)\n", cookie, iref2);
        goto bail;
    }
    iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2);

    /* pop two off at once */
    dvmPopIndirectRefTableSegment(&irt, segmentState[1]);
    cookie = segmentState[0];

    if (dvmIndirectRefTableEntries(&irt) != 2) {
        LOGE("Unexpected entry count in multiseg\n");
        goto bail;
    }
    dvmRemoveFromIndirectRefTable(&irt, cookie, iref0);
    dvmRemoveFromIndirectRefTable(&irt, cookie, iref1);
    if (dvmIndirectRefTableEntries(&irt) != 0) {
        LOGE("Unexpected entry count at multiseg end\n");
        goto bail;
    }

    DBUG_MSG("+++ segment test complete\n");
    result = true;

bail:
    dvmClearIndirectRefTable(&irt);
    return result;
}
예제 #7
0
/*
 * "buf" contains a full JDWP packet, possibly with multiple chunks.  We
 * need to process each, accumulate the replies, and ship the whole thing
 * back.
 *
 * Returns "true" if we have a reply.  The reply buffer is newly allocated,
 * and includes the chunk type/length, followed by the data.
 *
 * TODO: we currently assume that the request and reply include a single
 * chunk.  If this becomes inconvenient we will need to adapt.
 */
bool dvmDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
    int* pReplyLen)
{
    Thread* self = dvmThreadSelf();
    const int kChunkHdrLen = 8;
    ArrayObject* dataArray = NULL;
    bool result = false;

    assert(dataLen >= 0);

    /*
     * Prep DdmServer.  We could throw this in gDvm.
     */
    ClassObject* ddmServerClass;
    Method* dispatch;

    ddmServerClass =
        dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/DdmServer;", NULL);
    if (ddmServerClass == NULL) {
        LOGW("Unable to find org.apache.harmony.dalvik.ddmc.DdmServer\n");
        goto bail;
    }
    dispatch = dvmFindDirectMethodByDescriptor(ddmServerClass, "dispatch",
                    "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
    if (dispatch == NULL) {
        LOGW("Unable to find DdmServer.dispatch\n");
        goto bail;
    }

    /*
     * Prep Chunk.
     */
    int chunkTypeOff, chunkDataOff, chunkOffsetOff, chunkLengthOff;
    ClassObject* chunkClass;
    chunkClass = dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/Chunk;", NULL);
    if (chunkClass == NULL) {
        LOGW("Unable to find org.apache.harmony.dalvik.ddmc.Chunk\n");
        goto bail;
    }
    chunkTypeOff = dvmFindFieldOffset(chunkClass, "type", "I");
    chunkDataOff = dvmFindFieldOffset(chunkClass, "data", "[B");
    chunkOffsetOff = dvmFindFieldOffset(chunkClass, "offset", "I");
    chunkLengthOff = dvmFindFieldOffset(chunkClass, "length", "I");
    if (chunkTypeOff < 0 || chunkDataOff < 0 ||
        chunkOffsetOff < 0 || chunkLengthOff < 0)
    {
        LOGW("Unable to find all chunk fields\n");
        goto bail;
    }

    /*
     * The chunk handlers are written in the Java programming language, so
     * we need to convert the buffer to a byte array.
     */
    dataArray = dvmAllocPrimitiveArray('B', dataLen, ALLOC_DEFAULT);
    if (dataArray == NULL) {
        LOGW("array alloc failed (%d)\n", dataLen);
        dvmClearException(self);
        goto bail;
    }
    memcpy(dataArray->contents, buf, dataLen);

    /*
     * Run through and find all chunks.  [Currently just find the first.]
     */
    unsigned int offset, length, type;
    type = get4BE((u1*)dataArray->contents + 0);
    length = get4BE((u1*)dataArray->contents + 4);
    offset = kChunkHdrLen;
    if (offset+length > (unsigned int) dataLen) {
        LOGW("WARNING: bad chunk found (len=%u pktLen=%d)\n", length, dataLen);
        goto bail;
    }

    /*
     * Call the handler.
     */
    JValue callRes;
    dvmCallMethod(self, dispatch, NULL, &callRes, type, dataArray, offset,
        length);
    if (dvmCheckException(self)) {
        LOGI("Exception thrown by dispatcher for 0x%08x\n", type);
        dvmLogExceptionStackTrace();
        dvmClearException(self);
        goto bail;
    }

    Object* chunk;
    ArrayObject* replyData;
    chunk = (Object*) callRes.l;
    if (chunk == NULL)
        goto bail;

    /*
     * Pull the pieces out of the chunk.  We copy the results into a
     * newly-allocated buffer that the caller can free.  We don't want to
     * continue using the Chunk object because nothing has a reference to it.
     * (If we do an alloc in here, we need to dvmAddTrackedAlloc it.)
     *
     * We could avoid this by returning type/data/offset/length and having
     * the caller be aware of the object lifetime issues, but that
     * integrates the JDWP code more tightly into the VM, and doesn't work
     * if we have responses for multiple chunks.
     *
     * So we're pretty much stuck with copying data around multiple times.
     */
    type = dvmGetFieldInt(chunk, chunkTypeOff);
    replyData = (ArrayObject*) dvmGetFieldObject(chunk, chunkDataOff);
    offset = dvmGetFieldInt(chunk, chunkOffsetOff);
    length = dvmGetFieldInt(chunk, chunkLengthOff);

    LOGV("DDM reply: type=0x%08x data=%p offset=%d length=%d\n",
        type, replyData, offset, length);

    if (length == 0 || replyData == NULL)
        goto bail;
    if (offset + length > replyData->length) {
        LOGW("WARNING: chunk off=%d len=%d exceeds reply array len %d\n",
            offset, length, replyData->length);
        goto bail;
    }

    u1* reply;
    reply = (u1*) malloc(length + kChunkHdrLen);
    if (reply == NULL) {
        LOGW("malloc %d failed\n", length+kChunkHdrLen);
        goto bail;
    }
    set4BE(reply + 0, type);
    set4BE(reply + 4, length);
    memcpy(reply+kChunkHdrLen, (const u1*)replyData->contents + offset, length);

    *pReplyBuf = reply;
    *pReplyLen = length + kChunkHdrLen;
    result = true;

    LOGV("dvmHandleDdm returning type=%.4s buf=%p len=%d\n",
        (char*) reply, reply, length);

bail:
    dvmReleaseTrackedAlloc((Object*) dataArray, NULL);
    return result;
}