/** @return true if we need to schedule a call to enqueue(). */ static bool enqueueReference(Object *reference) { #if FANCY_REFERENCE_SUBCLASS /* See if this reference class has overridden enqueue(); * if not, we can take a shortcut. */ if (reference->clazz->vtable[gDvm.voffJavaLangRefReference_enqueue]->clazz == gDvm.classJavaLangRefReference) #endif { Object *queue = dvmGetFieldObject(reference, gDvm.offJavaLangRefReference_queue); Object *queueNext = dvmGetFieldObject(reference, gDvm.offJavaLangRefReference_queueNext); if (queue == NULL || queueNext != NULL) { /* There is no queue, or the reference has already * been enqueued. The Reference.enqueue() method * will do nothing even if we call it. */ return false; } } /* We need to call enqueue(), but if we called it from * here we'd probably deadlock. Schedule a call. */ return true; }
/* * Print the direct stack trace of the given exception to the log. */ static void logStackTraceOf(Object* exception) { const ArrayObject* stackData; StringObject* messageStr; int stackSize; const int* intVals; messageStr = (StringObject*) dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_message); if (messageStr != NULL) { char* cp = dvmCreateCstrFromString(messageStr); LOGI("%s: %s\n", exception->clazz->descriptor, cp); free(cp); } else { LOGI("%s:\n", exception->clazz->descriptor); } stackData = (const ArrayObject*) dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_stackState); if (stackData == NULL) { LOGI(" (no stack trace data found)\n"); return; } stackSize = stackData->length / 2; intVals = (const int*) stackData->contents; dvmLogRawStackTrace(intVals, stackSize); }
void ObjTracer::extract_activInfo( const Object* const obj ) { InstField* pF; ClassObject * clazz = obj->clazz; Object* o; fprintf( traceFile_, "instUid %u #%d: %p\n", opcodeTracer.get_instUid(), CMPNM_OBJ, obj ); //taskAffinity in ActivityInfo pF = &clazz->ifields[0]; o = dvmGetFieldObject( obj, pF->byteOffset ); this -> extract_str( o ); clazz = clazz->super; //processName in ComponentInfo pF = &clazz->ifields[1]; o = dvmGetFieldObject( obj, pF->byteOffset ); this -> extract_str( o ); clazz = clazz->super; //packageName in PackageItemInfo pF = &clazz->ifields[0]; o = dvmGetFieldObject( obj, pF->byteOffset ); this -> extract_str( o ); //name in PackageItemInfo pF = &clazz->ifields[0]; o = dvmGetFieldObject( obj, pF->byteOffset ); this -> extract_str( o ); //fprintf(f, "\n"); fflush(traceFile_); }
/* Mark all objects referred to by a DataObject's instance fields. */ static void scanInstanceFields(const DataObject *obj, ClassObject *clazz, GcMarkContext *ctx) { //TODO: Optimize this by avoiding walking the superclass chain while (clazz != NULL) { InstField *f; int i; /* All of the fields that contain object references * are guaranteed to be at the beginning of the ifields list. */ f = clazz->ifields; for (i = 0; i < clazz->ifieldRefCount; i++) { /* Mark the array or object reference. * May be NULL. * * Note that, per the comment on struct InstField, * f->byteOffset is the offset from the beginning of * obj, not the offset into obj->instanceData. */ markObject(dvmGetFieldObject((Object*)obj, f->byteOffset), ctx); f++; } /* This will be NULL when we hit java.lang.Object */ clazz = clazz->super; } }
/* Returns the taint on an object. * - Currently only arrays and java.lang.String is supported */ u4 getObjectTaint(Object* obj, const char* descriptor) { ArrayObject *arrObj = NULL; if (obj == NULL) { return TAINT_CLEAR; } if (descriptor[0] == '[') { /* Get the taint from the array */ arrObj = (ArrayObject*) obj; if (arrObj != NULL) { return arrObj->taint.tag; } } if (strcmp(descriptor, "Ljava/lang/String;") == 0) { arrObj = (ArrayObject*) dvmGetFieldObject(obj, gDvm.offJavaLangString_value); if (arrObj != NULL) { return arrObj->taint.tag; } /* else, empty string? don't worry about it */ } /* TODO: What about classes derived from String? */ /* Don't worry about other object types */ return TAINT_CLEAR; }
/* * public char charAt(int index) */ bool javaLangString_charAt(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult) { int count, offset; ArrayObject* chars; /* null reference check on "this" */ if ((Object*) arg0 == NULL) { dvmThrowNullPointerException(NULL); return false; } //ALOGI("String.charAt this=0x%08x index=%d", arg0, arg1); count = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT); if ((s4) arg1 < 0 || (s4) arg1 >= count) { dvmThrowStringIndexOutOfBoundsExceptionWithIndex(count, arg1); return false; } else { offset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET); chars = (ArrayObject*) dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE); pResult->i = ((const u2*)(void*)chars->contents)[arg1 + offset]; return true; } }
/* Process all enqueued heap work, including finalizers and reference * enqueueing. Clearing has already been done by the VM. * * Caller must hold gDvm.heapWorkerLock. */ static void doHeapWork(Thread *self) { Object *obj; HeapWorkerOperation op; int numFinalizersCalled, numReferencesEnqueued; assert(gDvm.voffJavaLangObject_finalize >= 0); assert(gDvm.methJavaLangRefReference_enqueueInternal != NULL); numFinalizersCalled = 0; numReferencesEnqueued = 0; while ((obj = dvmGetNextHeapWorkerObject(&op)) != NULL) { Method *method = NULL; /* Make sure the object hasn't been collected since * being scheduled. */ assert(dvmIsValidObject(obj)); /* Call the appropriate method(s). */ if (op == WORKER_FINALIZE) { numFinalizersCalled++; method = obj->clazz->vtable[gDvm.voffJavaLangObject_finalize]; assert(dvmCompareNameDescriptorAndMethod("finalize", "()V", method) == 0); assert(method->clazz != gDvm.classJavaLangObject); callMethod(self, obj, method); } else { assert(op == WORKER_ENQUEUE); assert(dvmGetFieldObject( obj, gDvm.offJavaLangRefReference_queue) != NULL); assert(dvmGetFieldObject( obj, gDvm.offJavaLangRefReference_queueNext) == NULL); numReferencesEnqueued++; callMethod(self, obj, gDvm.methJavaLangRefReference_enqueueInternal); } /* Let the GC collect the object. */ dvmReleaseTrackedAlloc(obj, self); } LOGV("Called %d finalizers\n", numFinalizersCalled); LOGV("Enqueued %d references\n", numReferencesEnqueued); }
/* * Send a notification when a thread starts or stops. * * Because we broadcast the full set of threads when the notifications are * first enabled, it's possible for "thread" to be actively executing. */ void dvmDdmSendThreadNotification(Thread* thread, bool started) { if (!gDvm.ddmThreadNotification) return; StringObject* nameObj = NULL; Object* threadObj = thread->threadObj; if (threadObj != NULL) { nameObj = (StringObject*) dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_name); } int type, len; u1 buf[256]; if (started) { const u2* chars; u2* outChars; size_t stringLen; type = CHUNK_TYPE("THCR"); if (nameObj != NULL) { stringLen = dvmStringLen(nameObj); chars = dvmStringChars(nameObj); } else { stringLen = 0; chars = NULL; } /* leave room for the two integer fields */ if (stringLen > (sizeof(buf) - sizeof(u4)*2) / 2) stringLen = (sizeof(buf) - sizeof(u4)*2) / 2; len = stringLen*2 + sizeof(u4)*2; set4BE(&buf[0x00], thread->threadId); set4BE(&buf[0x04], stringLen); /* copy the UTF-16 string, transforming to big-endian */ outChars = (u2*) &buf[0x08]; while (stringLen--) set2BE((u1*) (outChars++), *chars++); } else { type = CHUNK_TYPE("THDE"); len = 4; set4BE(&buf[0x00], thread->threadId); } dvmDbgDdmSendChunk(type, len, buf); }
/* Mark all objects referred to by a DataObject's instance fields. */ static void scanInstanceFields(const DataObject *obj, ClassObject *clazz, GcMarkContext *ctx) { if (clazz->refOffsets != CLASS_WALK_SUPER) { unsigned int refOffsets = clazz->refOffsets; while (refOffsets != 0) { const int rshift = CLZ(refOffsets); refOffsets &= ~(CLASS_HIGH_BIT >> rshift); markObject(dvmGetFieldObject((Object*)obj, CLASS_OFFSET_FROM_CLZ(rshift)), ctx); } } else {
/* * Get the "cause" field from an exception. * * The Throwable class initializes the "cause" field to "this" to * differentiate between being initialized to null and never being * initialized. We check for that here and convert it to NULL. */ Object* dvmGetExceptionCause(const Object* exception) { if (!dvmInstanceof(exception->clazz, gDvm.classJavaLangThrowable)) { LOGE("Tried to get cause from object of type '%s'\n", exception->clazz->descriptor); dvmAbort(); } Object* cause = dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_cause); if (cause == exception) return NULL; else return cause; }
/* Returns the object pointer for a field * - obj only used if the field is not static * - Note: will not return an array object */ Object* getObjectFromField(Field* field, Object* obj) { if (field->signature[0] != 'L') { return NULL; } if (dvmIsStaticField(field)) { StaticField* sfield = (StaticField*) field; return dvmGetStaticFieldObject(sfield); } else { InstField* ifield = (InstField*) field; return dvmGetFieldObject(obj, ifield->byteOffset); } }
/* * Utility function when we're evaluating alternative implementations. */ static void badMatch(StringObject* thisStrObj, StringObject* compStrObj, int expectResult, int newResult, const char* compareType) { ArrayObject* thisArray; ArrayObject* compArray; const char* thisStr; const char* compStr; int thisOffset, compOffset, thisCount, compCount; thisCount = dvmGetFieldInt((Object*) thisStrObj, STRING_FIELDOFF_COUNT); compCount = dvmGetFieldInt((Object*) compStrObj, STRING_FIELDOFF_COUNT); thisOffset = dvmGetFieldInt((Object*) thisStrObj, STRING_FIELDOFF_OFFSET); compOffset = dvmGetFieldInt((Object*) compStrObj, STRING_FIELDOFF_OFFSET); thisArray = (ArrayObject*) dvmGetFieldObject((Object*) thisStrObj, STRING_FIELDOFF_VALUE); compArray = (ArrayObject*) dvmGetFieldObject((Object*) compStrObj, STRING_FIELDOFF_VALUE); thisStr = dvmCreateCstrFromString(thisStrObj); compStr = dvmCreateCstrFromString(compStrObj); ALOGE("%s expected %d got %d", compareType, expectResult, newResult); ALOGE(" this (o=%d l=%d) '%s'", thisOffset, thisCount, thisStr); ALOGE(" comp (o=%d l=%d) '%s'", compOffset, compCount, compStr); dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG, ((const u2*) thisArray->contents) + thisOffset, thisCount*2, kHexDumpLocal); dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG, ((const u2*) compArray->contents) + compOffset, compCount*2, kHexDumpLocal); dvmAbort(); }
/* * JNI reflection support: convert reflection object to Method ptr. */ Method* dvmGetMethodFromReflectObj(Object* obj) { ClassObject* clazz; int slot; if (obj->clazz == gDvm.classJavaLangReflectConstructor) { clazz = (ClassObject*)dvmGetFieldObject(obj, gDvm.offJavaLangReflectConstructor_declClass); slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectConstructor_slot); } else if (obj->clazz == gDvm.classJavaLangReflectMethod) { clazz = (ClassObject*)dvmGetFieldObject(obj, gDvm.offJavaLangReflectMethod_declClass); slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectMethod_slot); } else { assert(false); return NULL; } /* must initialize the class before returning a method ID */ if (!dvmInitClass(clazz)) return NULL; return dvmSlotToMethod(clazz, slot); }
/* * Scans instance fields. */ static void scanInstanceFields(const Object *obj, GcMarkContext *ctx) { assert(obj != NULL); assert(obj->clazz != NULL); assert(ctx != NULL); if (obj->clazz->refOffsets != CLASS_WALK_SUPER) { unsigned int refOffsets = obj->clazz->refOffsets; while (refOffsets != 0) { const int rshift = CLZ(refOffsets); refOffsets &= ~(CLASS_HIGH_BIT >> rshift); markObject(dvmGetFieldObject((Object*)obj, CLASS_OFFSET_FROM_CLZ(rshift)), ctx); } } else {
/* Sets the taint on the return value * - rtaint points to an address in the args array * - descriptor is the return type * - for return objects, only arrays and java.lang.String supported * (will taint object reference returned otherwise) */ void setReturnTaint(u4 tag, u4* rtaint, JValue* pResult, const char* descriptor) { Object* obj = NULL; ArrayObject* arrObj = NULL; switch (descriptor[0]) { case 'V': /* void, do nothing */ break; case 'Z': case 'B': case 'C': case 'S': case 'I': case 'J': case 'F': case 'D': /* Easy case */ *rtaint |= tag; break; case '[': /* Best we can do is taint the array, however * this is not right for "[[" or "[L" */ arrObj = (ArrayObject*) pResult->l; if (arrObj != NULL) { arrObj->taint.tag |= tag; } /* else, method returning null pointer */ break; case 'L': obj = (Object*) pResult->l; if (obj != NULL) { if (strcmp(descriptor, "Ljava/lang/String;") == 0) { arrObj = (ArrayObject*)dvmGetFieldObject(obj, gDvm.offJavaLangString_value); if (arrObj != NULL) { arrObj->taint.tag |= tag; } /* else, empty string?, don't worry about it */ } else { /* TODO: What about classes derived from String? */ /* Best we can do is to taint the object ref */ *rtaint |= tag; } } break; } }
/* * JNI reflection support: convert reflection object to Field ptr. */ Field* dvmGetFieldFromReflectObj(Object* obj) { ClassObject* clazz; int slot; assert(obj->clazz == gDvm.classJavaLangReflectField); clazz = (ClassObject*)dvmGetFieldObject(obj, gDvm.offJavaLangReflectField_declClass); slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectField_slot); /* must initialize the class before returning a field ID */ if (!dvmInitClass(clazz)) return NULL; return dvmSlotToField(clazz, slot); }
void ObjTracer::extract_location(const Object * const obj) { fprintf( traceFile_, "instUid %u #%d: %p\n", opcodeTracer.get_instUid(), LOCAT_OBJ, obj); //longitude InstField *pF = &obj->clazz->ifields[6]; union myunion m; m.d = dvmGetFieldDouble( obj, pF->byteOffset ); this->record_normal('D', m.u); //latitude pF = &obj->clazz->ifields[9]; m.d = dvmGetFieldDouble( obj, pF->byteOffset ); this->record_normal('D', m.u); //lng2 in cache pF = &obj->clazz->ifields[7]; m.d = dvmGetFieldDouble( obj, pF->byteOffset ); this->record_normal('D', m.u); //lat2 in cache pF = &obj->clazz->ifields[11]; m.d = dvmGetFieldDouble( obj, pF->byteOffset ); this->record_normal('D', m.u); //lng1 in cache pF = &obj->clazz->ifields[8]; m.d = dvmGetFieldDouble( obj, pF->byteOffset ); this->record_normal('D', m.u); //lat1 in cache pF = &obj->clazz->ifields[10]; m.d = dvmGetFieldDouble( obj, pF->byteOffset ); this->record_normal('D', m.u); //provider: GPS or Network pF = &obj->clazz->ifields[1]; Object *o = dvmGetFieldObject ( obj, pF->byteOffset ); this->extract_str( o ); //fprintf(f, "\n"); fflush(traceFile_); }
void ObjTracer::extract_compName( const Object * const obj ) { InstField* pF; Object* o; fprintf( traceFile_, "instUid %u #%d: %p\n", opcodeTracer.get_instUid(), CMPNM_OBJ, obj ); for ( int i = 0; i < 2; i++ ) { pF = &obj->clazz->ifields[i]; o = dvmGetFieldObject( obj, pF->byteOffset ); this->extract_str(o); } //fprintf(f, "\n"); fflush(traceFile_); }
/* * Determine the index of the first character matching "ch". The string * to search is described by "chars", "offset", and "count". * * The character must be <= 0xffff. Supplementary characters are handled in * Java. * * The "start" parameter must be clamped to [0..count]. * * Returns -1 if no match is found. */ static inline int indexOfCommon(Object* strObj, int ch, int start) { //if ((ch & 0xffff) != ch) /* 32-bit code point */ // return -1; /* pull out the basic elements */ ArrayObject* charArray = (ArrayObject*) dvmGetFieldObject(strObj, STRING_FIELDOFF_VALUE); const u2* chars = (const u2*)(void*)charArray->contents; int offset = dvmGetFieldInt(strObj, STRING_FIELDOFF_OFFSET); int count = dvmGetFieldInt(strObj, STRING_FIELDOFF_COUNT); //ALOGI("String.indexOf(0x%08x, 0x%04x, %d) off=%d count=%d", // (u4) strObj, ch, start, offset, count); /* factor out the offset */ chars += offset; if (start < 0) start = 0; else if (start > count) start = count; #if 0 /* 16-bit loop, simple */ while (start < count) { if (chars[start] == ch) return start; start++; } #else /* 16-bit loop, slightly better on ARM */ const u2* ptr = chars + start; const u2* endPtr = chars + count; while (ptr < endPtr) { if (*ptr++ == ch) return (ptr-1) - chars; } #endif return -1; }
void ObjTracer::extract_procRecord( const Object* const obj ) { fprintf( traceFile_, "instUid %u #%d: %p\n", opcodeTracer.get_instUid(), PROREC_OBJ, obj ); //processName InstField* pF = &obj->clazz->ifields[20]; Object* o = dvmGetFieldObject( obj, pF->byteOffset ); this->extract_str(o); //pid pF = &obj->clazz->ifields[58]; u4 i = dvmGetFieldInt( obj, pF->byteOffset ); this->record_normal('I', &i ); //uid pF = &obj->clazz->ifields[77]; i = dvmGetFieldInt( obj, pF->byteOffset ); this->record_normal('I', &i ); //fprintf(f, "\n"); fflush(traceFile_); }
void ObjTracer::extract_intent( Object * obj ) { InstField *pF1, *pF2; Object *o1, *o2; fprintf( traceFile_, "instUid %u #%d: %p\n", opcodeTracer.get_instUid(), INTENT_OBJ, obj ); // mAction in Intent pF1 = &obj->clazz->ifields[0]; o1 = dvmGetFieldObject ( obj, pF1->byteOffset ); this->extract_str(o1); //mComponent in Intent pF1 = &obj->clazz->ifields[3]; o1 = dvmGetFieldObject ( obj, pF1->byteOffset ); { if (check_obj(o1)) { //mClass in mComponent pF2 = &o1->clazz->ifields[0]; o2 = dvmGetFieldObject ( o1, pF2->byteOffset ); this->extract_str(o2); //mPackage in mComponent pF2 = &o1->clazz->ifields[1]; o2 = dvmGetFieldObject ( o1, pF2->byteOffset ); this->extract_str(o2); } else fprintf(traceFile_, "\n\n"); } //mType in Intent pF1 = &obj->clazz->ifields[6]; o1 = dvmGetFieldObject ( obj, pF1->byteOffset ); this->extract_str(o1); //mPackage in Intent pF1 = &obj->clazz->ifields[7]; o1 = dvmGetFieldObject( obj, pF1->byteOffset ); this->extract_str(o1); //fprintf(f, "\n"); fflush(traceFile_); }
void ObjTracer::extract_activRecord( const Object * const obj ) { InstField *pF, *pF1; Object *o, *o1; fprintf( traceFile_, "instUid %u #%d: %p\n", opcodeTracer.get_instUid(), ACTREC_OBJ, obj ); //baseDir pF = &obj->clazz->ifields[2]; o = dvmGetFieldObject ( obj, pF->byteOffset ); this->extract_str( o ); //taskAffinity pF = &obj->clazz->ifields[6]; o = dvmGetFieldObject ( obj, pF->byteOffset ); this->extract_str( o ); //dataDir pF = &obj->clazz->ifields[10]; o = dvmGetFieldObject ( obj, pF->byteOffset ); this->extract_str( o ); //stringName pF = &obj->clazz->ifields[11]; o = dvmGetFieldObject ( obj, pF->byteOffset ); this->extract_str( o ); //shortComponentName pF = &obj->clazz->ifields[14]; o = dvmGetFieldObject ( obj, pF->byteOffset ); this->extract_str( o ); //resDir pF = &obj->clazz->ifields[21]; o = dvmGetFieldObject ( obj, pF->byteOffset ); this->extract_str( o ); //realActivity pF = &obj->clazz->ifields[22]; o = dvmGetFieldObject ( obj, pF->byteOffset ); if ( check_obj(o) ) { //mClass in mComponent pF1 = &o->clazz->ifields[0]; o1 = dvmGetFieldObject ( o, pF1->byteOffset ); this->extract_str(o1); //mPackage in mComponent pF1 = &o->clazz->ifields[1]; o1 = dvmGetFieldObject ( o, pF1->byteOffset ); this->extract_str(o1); } else fprintf(traceFile_, "\n\n"); //processName pF = &obj->clazz->ifields[23]; o = dvmGetFieldObject ( obj, pF->byteOffset ); this->extract_str( o ); //intent pF = &obj->clazz->ifields[25]; o = dvmGetFieldObject ( obj, pF->byteOffset ); if (check_obj(o)) { pF1 = &o->clazz->ifields[0]; o1 = dvmGetFieldObject( o, pF1->byteOffset ); this->extract_str(o1); } else fprintf(traceFile_, "\n"); //packageName pF = &obj->clazz->ifields[28]; o = dvmGetFieldObject ( obj, pF->byteOffset ); this->extract_str( o ); //launchedFromUid pF = &obj->clazz->ifields[61]; u4 i = dvmGetFieldInt( obj, pF->byteOffset ); this->record_normal('I', &i ); //fprintf(f, "\n"); fflush(traceFile_); }
/* * "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; }
/* * This is the common message body for proxy methods. * * The method we're calling looks like: * public Object invoke(Object proxy, Method method, Object[] args) * * This means we have to create a Method object, box our arguments into * a new Object[] array, make the call, and unbox the return value if * necessary. */ static void proxyInvoker(const u4* args, JValue* pResult, const Method* method, Thread* self) { Object* thisObj = (Object*) args[0]; Object* methodObj = NULL; ArrayObject* argArray = NULL; Object* handler; Method* invoke; ClassObject* returnType; int hOffset; JValue invokeResult; /* * Retrieve handler object for this proxy instance. */ hOffset = dvmFindFieldOffset(thisObj->clazz, "h", "Ljava/lang/reflect/InvocationHandler;"); if (hOffset < 0) { LOGE("Unable to find 'h' in Proxy object\n"); dvmAbort(); } handler = dvmGetFieldObject(thisObj, hOffset); /* * Find the invoke() method, looking in "this"s class. (Because we * start here we don't have to convert it to a vtable index and then * index into this' vtable.) */ invoke = dvmFindVirtualMethodHierByDescriptor(handler->clazz, "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"); if (invoke == NULL) { LOGE("Unable to find invoke()\n"); dvmAbort(); } LOGV("invoke: %s.%s, this=%p, handler=%s\n", method->clazz->descriptor, method->name, thisObj, handler->clazz->descriptor); /* * Create a java.lang.reflect.Method object for this method. * * We don't want to use "method", because that's the concrete * implementation in the proxy class. We want the abstract Method * from the declaring interface. We have a pointer to it tucked * away in the "insns" field. * * TODO: this could be cached for performance. */ methodObj = dvmCreateReflectMethodObject((Method*) method->insns); if (methodObj == NULL) { assert(dvmCheckException(self)); goto bail; } /* * Determine the return type from the signature. * * TODO: this could be cached for performance. */ returnType = dvmGetBoxedReturnType(method); if (returnType == NULL) { char* desc = dexProtoCopyMethodDescriptor(&method->prototype); LOGE("Could not determine return type for '%s'\n", desc); free(desc); assert(dvmCheckException(self)); goto bail; } LOGV(" return type will be %s\n", returnType->descriptor); /* * Convert "args" array into Object[] array, using the method * signature to determine types. If the method takes no arguments, * we must pass null. */ argArray = boxMethodArgs(method, args+1); if (dvmCheckException(self)) goto bail; /* * Call h.invoke(proxy, method, args). * * We don't need to repackage exceptions, so if one has been thrown * just jump to the end. */ dvmCallMethod(self, invoke, handler, &invokeResult, thisObj, methodObj, argArray); if (dvmCheckException(self)) goto bail; /* * Unbox the return value. If it's the wrong type, throw a * ClassCastException. If it's a null pointer and we need a * primitive type, throw a NullPointerException. */ if (returnType->primitiveType == PRIM_VOID) { LOGVV("+++ ignoring return to void\n"); } else if (invokeResult.l == NULL) { if (dvmIsPrimitiveClass(returnType)) { dvmThrowException("Ljava/lang/NullPointerException;", "null result when primitive expected"); goto bail; } pResult->l = NULL; } else { if (!dvmUnwrapPrimitive(invokeResult.l, returnType, pResult)) { dvmThrowExceptionWithClassMessage("Ljava/lang/ClassCastException;", ((Object*)invokeResult.l)->clazz->descriptor); goto bail; } } bail: dvmReleaseTrackedAlloc(methodObj, self); dvmReleaseTrackedAlloc((Object*)argArray, self); }
/* * public int compareTo(String s) */ bool javaLangString_compareTo(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult) { /* * Null reference check on "this". Normally this is performed during * the setup of the virtual method call. We need to do it before * anything else. While we're at it, check out the other string, * which must also be non-null. */ if ((Object*) arg0 == NULL || (Object*) arg1 == NULL) { dvmThrowNullPointerException(NULL); return false; } /* quick test for comparison with itself */ if (arg0 == arg1) { pResult->i = 0; return true; } /* * This would be simpler and faster if we promoted StringObject to * a full representation, lining up the C structure fields with the * actual object fields. */ int thisCount, thisOffset, compCount, compOffset; ArrayObject* thisArray; ArrayObject* compArray; const u2* thisChars; const u2* compChars; int minCount, countDiff; thisCount = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT); compCount = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_COUNT); countDiff = thisCount - compCount; minCount = (countDiff < 0) ? thisCount : compCount; thisOffset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET); compOffset = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_OFFSET); thisArray = (ArrayObject*) dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE); compArray = (ArrayObject*) dvmGetFieldObject((Object*) arg1, STRING_FIELDOFF_VALUE); thisChars = ((const u2*)(void*)thisArray->contents) + thisOffset; compChars = ((const u2*)(void*)compArray->contents) + compOffset; #ifdef HAVE__MEMCMP16 /* * Use assembly version, which returns the difference between the * characters. The annoying part here is that 0x00e9 - 0xffff != 0x00ea, * because the interpreter converts the characters to 32-bit integers * *without* sign extension before it subtracts them (which makes some * sense since "char" is unsigned). So what we get is the result of * 0x000000e9 - 0x0000ffff, which is 0xffff00ea. */ int otherRes = __memcmp16(thisChars, compChars, minCount); # ifdef CHECK_MEMCMP16 int i; for (i = 0; i < minCount; i++) { if (thisChars[i] != compChars[i]) { pResult->i = (s4) thisChars[i] - (s4) compChars[i]; if (pResult->i != otherRes) { badMatch((StringObject*) arg0, (StringObject*) arg1, pResult->i, otherRes, "compareTo"); } return true; } } # endif if (otherRes != 0) { pResult->i = otherRes; return true; } #else /* * Straightforward implementation, examining 16 bits at a time. Compare * the characters that overlap, and if they're all the same then return * the difference in lengths. */ int i; for (i = 0; i < minCount; i++) { if (thisChars[i] != compChars[i]) { pResult->i = (s4) thisChars[i] - (s4) compChars[i]; return true; } } #endif pResult->i = countDiff; return true; }
/* All objects for stronger reference levels have been * marked before this is called. */ void dvmHeapHandleReferences(Object *refListHead, enum RefType refType) { Object *reference; GcMarkContext *markContext = &gDvm.gcHeap->markContext; const int offVmData = gDvm.offJavaLangRefReference_vmData; const int offReferent = gDvm.offJavaLangRefReference_referent; bool workRequired = false; size_t numCleared = 0; size_t numEnqueued = 0; reference = refListHead; while (reference != NULL) { Object *next; Object *referent; /* Pull the interesting fields out of the Reference object. */ next = dvmGetFieldObject(reference, offVmData); referent = dvmGetFieldObject(reference, offReferent); //TODO: when handling REF_PHANTOM, unlink any references // that fail this initial if(). We need to re-walk // the list, and it would be nice to avoid the extra // work. if (referent != NULL && !isMarked(ptr2chunk(referent), markContext)) { bool schedClear, schedEnqueue; /* This is the strongest reference that refers to referent. * Do the right thing. */ switch (refType) { case REF_SOFT: case REF_WEAK: schedClear = clearReference(reference); schedEnqueue = enqueueReference(reference); break; case REF_PHANTOM: /* PhantomReferences are not cleared automatically. * Until someone clears it (or the reference itself * is collected), the referent must remain alive. * * It's necessary to fully mark the referent because * it will still be present during the next GC, and * all objects that it points to must be valid. * (The referent will be marked outside of this loop, * after handing all references of this strength, in * case multiple references point to the same object.) */ schedClear = false; /* A PhantomReference is only useful with a * queue, but since it's possible to create one * without a queue, we need to check. */ schedEnqueue = enqueueReference(reference); break; default: assert(!"Bad reference type"); schedClear = false; schedEnqueue = false; break; } numCleared += schedClear ? 1 : 0; numEnqueued += schedEnqueue ? 1 : 0; if (schedClear || schedEnqueue) { uintptr_t workBits; /* Stuff the clear/enqueue bits in the bottom of * the pointer. Assumes that objects are 8-byte * aligned. * * Note that we are adding the *Reference* (which * is by definition already marked at this point) to * this list; we're not adding the referent (which * has already been cleared). */ assert(((intptr_t)reference & 3) == 0); assert(((WORKER_CLEAR | WORKER_ENQUEUE) & ~3) == 0); workBits = (schedClear ? WORKER_CLEAR : 0) | (schedEnqueue ? WORKER_ENQUEUE : 0); if (!dvmHeapAddRefToLargeTable( &gDvm.gcHeap->referenceOperations, (Object *)((uintptr_t)reference | workBits))) { LOGE_HEAP("dvmMalloc(): no room for any more " "reference operations\n"); dvmAbort(); } workRequired = true; } if (refType != REF_PHANTOM) { /* Let later GCs know not to reschedule this reference. */ dvmSetFieldObject(reference, offVmData, SCHEDULED_REFERENCE_MAGIC); } // else this is handled later for REF_PHANTOM } // else there was a stronger reference to the referent. reference = next; } #define refType2str(r) \ ((r) == REF_SOFT ? "soft" : ( \ (r) == REF_WEAK ? "weak" : ( \ (r) == REF_PHANTOM ? "phantom" : "UNKNOWN" ))) LOGD_HEAP("dvmHeapHandleReferences(): cleared %zd, enqueued %zd %s references\n", numCleared, numEnqueued, refType2str(refType)); /* Walk though the reference list again, and mark any non-clear/marked * referents. Only PhantomReferences can have non-clear referents * at this point. */ if (refType == REF_PHANTOM) { bool scanRequired = false; HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_REFERENCE_CLEANUP, 0); reference = refListHead; while (reference != NULL) { Object *next; Object *referent; /* Pull the interesting fields out of the Reference object. */ next = dvmGetFieldObject(reference, offVmData); referent = dvmGetFieldObject(reference, offReferent); if (referent != NULL && !isMarked(ptr2chunk(referent), markContext)) { markObjectNonNull(referent, markContext); scanRequired = true; /* Let later GCs know not to reschedule this reference. */ dvmSetFieldObject(reference, offVmData, SCHEDULED_REFERENCE_MAGIC); } reference = next; } HPROF_CLEAR_GC_SCAN_STATE(); if (scanRequired) { processMarkStack(markContext); } } if (workRequired) { dvmSignalHeapWorker(false); } }
/* * public boolean equals(Object anObject) */ bool javaLangString_equals(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult) { /* * Null reference check on "this". */ if ((Object*) arg0 == NULL) { dvmThrowNullPointerException(NULL); return false; } /* quick test for comparison with itself */ if (arg0 == arg1) { pResult->i = true; return true; } /* * See if the other object is also a String. * * str.equals(null) is expected to return false, presumably based on * the results of the instanceof test. */ if (arg1 == 0 || ((Object*) arg0)->clazz != ((Object*) arg1)->clazz) { pResult->i = false; return true; } /* * This would be simpler and faster if we promoted StringObject to * a full representation, lining up the C structure fields with the * actual object fields. */ int thisCount, thisOffset, compCount, compOffset; ArrayObject* thisArray; ArrayObject* compArray; const u2* thisChars; const u2* compChars; /* quick length check */ thisCount = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT); compCount = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_COUNT); if (thisCount != compCount) { pResult->i = false; return true; } /* * You may, at this point, be tempted to pull out the hashCode fields * and compare them. If both fields have been initialized, and they * are not equal, we can return false immediately. * * However, the hashCode field is often not set. If it is set, * there's an excellent chance that the String is being used as a key * in a hashed data structure (e.g. HashMap). That data structure has * already made the comparison and determined that the hashes are equal, * making a check here redundant. * * It's not clear that checking the hashes will be a win in "typical" * use cases. We err on the side of simplicity and ignore them. */ thisOffset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET); compOffset = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_OFFSET); thisArray = (ArrayObject*) dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE); compArray = (ArrayObject*) dvmGetFieldObject((Object*) arg1, STRING_FIELDOFF_VALUE); thisChars = ((const u2*)(void*)thisArray->contents) + thisOffset; compChars = ((const u2*)(void*)compArray->contents) + compOffset; #ifdef HAVE__MEMCMP16 pResult->i = (__memcmp16(thisChars, compChars, thisCount) == 0); # ifdef CHECK_MEMCMP16 int otherRes = (memcmp(thisChars, compChars, thisCount * 2) == 0); if (pResult->i != otherRes) { badMatch((StringObject*) arg0, (StringObject*) arg1, otherRes, pResult->i, "equals-1"); } # endif #else /* * Straightforward implementation, examining 16 bits at a time. The * direction of the loop doesn't matter, and starting at the end may * give us an advantage when comparing certain types of strings (e.g. * class names). * * We want to go forward for benchmarks against __memcmp16 so we get a * meaningful comparison when the strings don't match (could also test * with palindromes). */ int i; //for (i = 0; i < thisCount; i++) for (i = thisCount-1; i >= 0; --i) { if (thisChars[i] != compChars[i]) { pResult->i = false; return true; } } pResult->i = true; #endif return true; }
/* Mark all objects that obj refers to. * * Called on every object in markList. */ static void scanObject(const Object *obj, GcMarkContext *ctx) { ClassObject *clazz; assert(dvmIsValidObject(obj)); LOGV_SCAN("0x%08x %s\n", (uint)obj, obj->clazz->name); #if WITH_HPROF if (gDvm.gcHeap->hprofContext != NULL) { hprofDumpHeapObject(gDvm.gcHeap->hprofContext, obj); } #endif /* Get and mark the class object for this particular instance. */ clazz = obj->clazz; if (clazz == NULL) { /* This can happen if we catch an object between * dvmMalloc() and DVM_OBJECT_INIT(). The object * won't contain any references yet, so we can * just skip it. */ return; } else if (clazz == gDvm.unlinkedJavaLangClass) { /* This class hasn't been linked yet. We're guaranteed * that the object doesn't contain any references that * aren't already tracked, so we can skip scanning it. * * NOTE: unlinkedJavaLangClass is not on the heap, so * it's very important that we don't try marking it. */ return; } #if WITH_OBJECT_HEADERS gMarkParent = obj; if (ptr2chunk(obj)->scanGeneration == gGeneration) { LOGE("object 0x%08x was already scanned this generation\n", (uintptr_t)obj); dvmAbort(); } ptr2chunk(obj)->oldScanGeneration = ptr2chunk(obj)->scanGeneration; ptr2chunk(obj)->scanGeneration = gGeneration; ptr2chunk(obj)->scanCount++; #endif assert(dvmIsValidObject((Object *)clazz)); markObjectNonNull((Object *)clazz, ctx); /* Mark any references in this object. */ if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) { /* It's an array object. */ if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) { /* It's an array of object references. */ scanObjectArray((ArrayObject *)obj, ctx); } // else there's nothing else to scan } else { /* It's a DataObject-compatible object. */ scanInstanceFields((DataObject *)obj, clazz, ctx); if (IS_CLASS_FLAG_SET(clazz, CLASS_ISREFERENCE)) { GcHeap *gcHeap = gDvm.gcHeap; Object *referent; /* It's a subclass of java/lang/ref/Reference. * The fields in this class have been arranged * such that scanInstanceFields() did not actually * mark the "referent" field; we need to handle * it specially. * * If the referent already has a strong mark (isMarked(referent)), * we don't care about its reference status. */ referent = dvmGetFieldObject(obj, gDvm.offJavaLangRefReference_referent); if (referent != NULL && !isMarked(ptr2chunk(referent), &gcHeap->markContext)) { u4 refFlags; if (gcHeap->markAllReferents) { LOG_REF("Hard-marking a reference\n"); /* Don't bother with normal reference-following * behavior, just mark the referent. This should * only be used when following objects that just * became scheduled for finalization. */ markObjectNonNull(referent, ctx); goto skip_reference; } /* See if this reference was handled by a previous GC. */ if (dvmGetFieldObject(obj, gDvm.offJavaLangRefReference_vmData) == SCHEDULED_REFERENCE_MAGIC) { LOG_REF("Skipping scheduled reference\n"); /* Don't reschedule it, but make sure that its * referent doesn't get collected (in case it's * a PhantomReference and wasn't cleared automatically). */ //TODO: Mark these after handling all new refs of // this strength, in case the new refs refer // to the same referent. Not a very common // case, though. markObjectNonNull(referent, ctx); goto skip_reference; } /* Find out what kind of reference is pointing * to referent. */ refFlags = GET_CLASS_FLAG_GROUP(clazz, CLASS_ISREFERENCE | CLASS_ISWEAKREFERENCE | CLASS_ISPHANTOMREFERENCE); /* We use the vmData field of Reference objects * as a next pointer in a singly-linked list. * That way, we don't need to allocate any memory * while we're doing a GC. */ #define ADD_REF_TO_LIST(list, ref) \ do { \ Object *ARTL_ref_ = (/*de-const*/Object *)(ref); \ dvmSetFieldObject(ARTL_ref_, \ gDvm.offJavaLangRefReference_vmData, list); \ list = ARTL_ref_; \ } while (false) /* At this stage, we just keep track of all of * the live references that we've seen. Later, * we'll walk through each of these lists and * deal with the referents. */ if (refFlags == CLASS_ISREFERENCE) { /* It's a soft reference. Depending on the state, * we'll attempt to collect all of them, some of * them, or none of them. */ if (gcHeap->softReferenceCollectionState == SR_COLLECT_NONE) { sr_collect_none: markObjectNonNull(referent, ctx); } else if (gcHeap->softReferenceCollectionState == SR_COLLECT_ALL) { sr_collect_all: ADD_REF_TO_LIST(gcHeap->softReferences, obj); } else { /* We'll only try to collect half of the * referents. */ if (gcHeap->softReferenceColor++ & 1) { goto sr_collect_none; } goto sr_collect_all; } } else { /* It's a weak or phantom reference. * Clearing CLASS_ISREFERENCE will reveal which. */ refFlags &= ~CLASS_ISREFERENCE; if (refFlags == CLASS_ISWEAKREFERENCE) { ADD_REF_TO_LIST(gcHeap->weakReferences, obj); } else if (refFlags == CLASS_ISPHANTOMREFERENCE) { ADD_REF_TO_LIST(gcHeap->phantomReferences, obj); } else { assert(!"Unknown reference type"); } } #undef ADD_REF_TO_LIST } } skip_reference: /* If this is a class object, mark various other things that * its internals point to. * * All class objects are instances of java.lang.Class, * including the java.lang.Class class object. */ if (clazz == gDvm.classJavaLangClass) { scanClassObject((ClassObject *)obj, ctx); } } #if WITH_OBJECT_HEADERS gMarkParent = NULL; #endif }