コード例 #1
0
ファイル: Heap.cpp プロジェクト: XClouded/fastiva
/*
 * If the concurrent GC is running, wait for it to finish.  The caller
 * must hold the heap lock.
 *
 * Note: the second dvmChangeStatus() could stall if we were in RUNNING
 * on entry, and some other thread has asked us to suspend.  In that
 * case we will be suspended with the heap lock held, which can lead to
 * deadlock if the other thread tries to do something with the managed heap.
 * For example, the debugger might suspend us and then execute a method that
 * allocates memory.  We can avoid this situation by releasing the lock
 * before self-suspending.  (The developer can work around this specific
 * situation by single-stepping the VM.  Alternatively, we could disable
 * concurrent GC when the debugger is attached, but that might change
 * behavior more than is desirable.)
 *
 * This should not be a problem in production, because any GC-related
 * activity will grab the lock before issuing a suspend-all.  (We may briefly
 * suspend when the GC thread calls dvmUnlockHeap before dvmResumeAllThreads,
 * but there's no risk of deadlock.)
 */
bool dvmWaitForConcurrentGcToComplete()
{
    ATRACE_BEGIN("GC: Wait For Concurrent");
    bool waited = gDvm.gcHeap->gcRunning;
    Thread *self = dvmThreadSelf();
    assert(self != NULL);
    u4 start = dvmGetRelativeTimeMsec();
#ifdef FASTIVA
	// Ensure no Java-object reference is used in local-stack.
	// and save Java-object reference maybe in registers.
	FASTIVA_SUSPEND_STACK_unsafe(self);
    ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
    while (gDvm.gcHeap->gcRunning) {
        dvmWaitCond(&gDvm.gcHeapCond, &gDvm.gcHeapLock);
    }
    dvmChangeStatus(self, oldStatus);
	FASTIVA_RESUME_STACK_unsafe(self);
#else
    while (gDvm.gcHeap->gcRunning) {
        ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
        dvmWaitCond(&gDvm.gcHeapCond, &gDvm.gcHeapLock);
        dvmChangeStatus(self, oldStatus);
    }
#endif
    u4 end = dvmGetRelativeTimeMsec();
    if (end - start > 0) {
        ALOGD("WAIT_FOR_CONCURRENT_GC blocked %ums", end - start);
    }
    ATRACE_END();
    return waited;
}
コード例 #2
0
/*
 * If the concurrent GC is running, wait for it to finish.  The caller
 * must hold the heap lock.
 *
 * Note: the second dvmChangeStatus() could stall if we were in RUNNING
 * on entry, and some other thread has asked us to suspend.  In that
 * case we will be suspended with the heap lock held, which can lead to
 * deadlock if the other thread tries to do something with the managed heap.
 * For example, the debugger might suspend us and then execute a method that
 * allocates memory.  We can avoid this situation by releasing the lock
 * before self-suspending.  (The developer can work around this specific
 * situation by single-stepping the VM.  Alternatively, we could disable
 * concurrent GC when the debugger is attached, but that might change
 * behavior more than is desirable.)
 *
 * This should not be a problem in production, because any GC-related
 * activity will grab the lock before issuing a suspend-all.  (We may briefly
 * suspend when the GC thread calls dvmUnlockHeap before dvmResumeAllThreads,
 * but there's no risk of deadlock.)
 */
void dvmWaitForConcurrentGcToComplete()
{
    Thread *self = dvmThreadSelf();
    assert(self != NULL);
    u4 start = dvmGetRelativeTimeMsec();
    while (gDvm.gcHeap->gcRunning) {
        ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
        dvmWaitCond(&gDvm.gcHeapCond, &gDvm.gcHeapLock);
        dvmChangeStatus(self, oldStatus);
    }
    u4 end = dvmGetRelativeTimeMsec();
    ALOGD("WAIT_FOR_CONCURRENT_GC blocked %ums", end - start);
}
コード例 #3
0
/*
 * Grab the lock, but put ourselves into THREAD_VMWAIT if it looks like
 * we're going to have to wait on the mutex.
 */
bool dvmLockHeap()
{
    if (dvmTryLockMutex(&gDvm.gcHeapLock) != 0) {
        Thread *self;
        ThreadStatus oldStatus;

        self = dvmThreadSelf();
        oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
        dvmLockMutex(&gDvm.gcHeapLock);
        dvmChangeStatus(self, oldStatus);
    }

    return true;
}
コード例 #4
0
/*
 * Acquires a mutex, transitioning to the VMWAIT state if the mutex is
 * held.  This allows the thread to suspend while it waits for another
 * thread to release the mutex.
 */
static void lockMutex(pthread_mutex_t *mu)
{
    Thread *self;
    ThreadStatus oldStatus;

    assert(mu != NULL);
    if (dvmTryLockMutex(mu) != 0) {
        self = dvmThreadSelf();
        assert(self != NULL);
        oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
        dvmLockMutex(mu);
        dvmChangeStatus(self, oldStatus);
    }
}
コード例 #5
0
ファイル: HeapSource.cpp プロジェクト: sawrus/dalvik
/*
 * The garbage collection daemon.  Initiates a concurrent collection
 * when signaled.  Also periodically trims the heaps when a few seconds
 * have elapsed since the last concurrent GC.
 */
static void *gcDaemonThread(void* arg)
{
    dvmChangeStatus(NULL, THREAD_VMWAIT);
    dvmLockMutex(&gHs->gcThreadMutex);
    while (gHs->gcThreadShutdown != true) {
        bool trim = false;
        if (gHs->gcThreadTrimNeeded) {
            int result = dvmRelativeCondWait(&gHs->gcThreadCond, &gHs->gcThreadMutex,
                                             HEAP_TRIM_IDLE_TIME_MS, 0);
            if (result == ETIMEDOUT) {
                /* Timed out waiting for a GC request, schedule a heap trim. */
                trim = true;
            }
        } else {
            dvmWaitCond(&gHs->gcThreadCond, &gHs->gcThreadMutex);
        }

        // Many JDWP requests cause allocation. We can't take the heap lock and wait to
        // transition to runnable so we can start a GC if a debugger is connected, because
        // we don't know that the JDWP thread isn't about to allocate and require the
        // heap lock itself, leading to deadlock. http://b/8191824.
        if (gDvm.debuggerConnected) {
            continue;
        }

        dvmLockHeap();
        /*
         * Another thread may have started a concurrent garbage
         * collection before we were scheduled.  Check for this
         * condition before proceeding.
         */
        if (!gDvm.gcHeap->gcRunning) {
            dvmChangeStatus(NULL, THREAD_RUNNING);
            if (trim) {
                trimHeaps();
                gHs->gcThreadTrimNeeded = false;
            } else {
                dvmCollectGarbageInternal(GC_CONCURRENT);
                gHs->gcThreadTrimNeeded = true;
            }
            dvmChangeStatus(NULL, THREAD_VMWAIT);
        }
        dvmUnlockHeap();
    }
    dvmChangeStatus(NULL, THREAD_RUNNING);
    return NULL;
}
コード例 #6
0
/*
 * private static void nativeExit(int code, boolean isExit)
 *
 * Runtime.exit() calls this after doing shutdown processing.  Runtime.halt()
 * uses this as well.
 */
static void Dalvik_java_lang_Runtime_nativeExit(const u4* args,
    JValue* pResult)
{
    int status = args[0];
    bool isExit = (args[1] != 0);

    if (isExit && gDvm.exitHook != NULL) {
        dvmChangeStatus(NULL, THREAD_NATIVE);
        (*gDvm.exitHook)(status);     // not expected to return
        dvmChangeStatus(NULL, THREAD_RUNNING);
        LOGW("JNI exit hook returned\n");
    }
    LOGD("Calling exit(%d)\n", status);
#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
    dvmCompilerDumpStats();
#endif
    exit(status);
}
コード例 #7
0
static void* coreValuesCatcherThreadStart(void* arg)
{
    Thread* self = dvmThreadSelf();

    UNUSED_PARAMETER(arg);

    while (true)  { //maybe have to put break somewhere
        dvmChangeStatus(self, THREAD_VMWAIT);

        dvmChangeStatus(self, THREAD_RUNNING);

        dvmSystemCoreValuesUpdate();
        /*Update each second*/
    	dvmThreadSleep(1000, 0);
    }

    return NULL;
}
コード例 #8
0
ファイル: Heap.cpp プロジェクト: acpaluri/591_Comet
/*
 * Grab the lock, but put ourselves into THREAD_VMWAIT if it looks like
 * we're going to have to wait on the mutex.
 */
bool dvmLockHeap()
{
    /* This is hacked a bit to avoid deadlocks.  Basically I don't want a thread
     * to suspend itself hodling the heap lock. */
    int res;
    while ((res = dvmTryLockMutex(&gDvm.gcHeapLock)) != 0) {
        assert(res == EBUSY);

        Thread *self;
        ThreadStatus oldStatus;

        self = dvmThreadSelf();
        oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
        dvmLockMutex(&gDvm.gcHeapLock);
        dvmUnlockMutex(&gDvm.gcHeapLock);
        dvmChangeStatus(self, oldStatus);
    }

    return true;
}
コード例 #9
0
/*
 * Block until all pending heap worker work has finished.
 */
void dvmWaitForHeapWorkerIdle()
{
    assert(gDvm.heapWorkerReady);

    dvmChangeStatus(NULL, THREAD_VMWAIT);

    dvmLockMutex(&gDvm.heapWorkerLock);

    /* Wake up the heap worker and wait for it to finish. */
    //TODO(http://b/issue?id=699704): This will deadlock if
    //     called from finalize(), enqueue(), or clear().  We
    //     need to detect when this is called from the HeapWorker
    //     context and just give up.
    dvmSignalHeapWorker(false);
    dvmWaitCond(&gDvm.heapWorkerIdleCond, &gDvm.heapWorkerLock);

    dvmUnlockMutex(&gDvm.heapWorkerLock);

    dvmChangeStatus(NULL, THREAD_RUNNING);
}
コード例 #10
0
ファイル: Heap.cpp プロジェクト: XClouded/fastiva
/*
 * Grab the lock, but put ourselves into THREAD_VMWAIT if it looks like
 * we're going to have to wait on the mutex.
 */
bool dvmLockHeap()
{
    if (dvmTryLockMutex(&gDvm.gcHeapLock) != 0) {
        Thread *self;
        ThreadStatus oldStatus;

        self = dvmThreadSelf();
#ifdef FASTIVA
		FASTIVA_SUSPEND_STACK_unsafe(self);
#endif
        oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
        dvmLockMutex(&gDvm.gcHeapLock);
        dvmChangeStatus(self, oldStatus);
#ifdef FASTIVA
		FASTIVA_RESUME_STACK_unsafe(self);
#endif
    }

    return true;
}
コード例 #11
0
/*
 * If the concurrent GC is running, wait for it to finish.  The caller
 * must hold the heap lock.
 *
 * Note: the second dvmChangeStatus() could stall if we were in RUNNING
 * on entry, and some other thread has asked us to suspend.  In that
 * case we will be suspended with the heap lock held, which can lead to
 * deadlock if the other thread tries to do something with the managed heap.
 * For example, the debugger might suspend us and then execute a method that
 * allocates memory.  We can avoid this situation by releasing the lock
 * before self-suspending.  (The developer can work around this specific
 * situation by single-stepping the VM.  Alternatively, we could disable
 * concurrent GC when the debugger is attached, but that might change
 * behavior more than is desirable.)
 *
 * This should not be a problem in production, because any GC-related
 * activity will grab the lock before issuing a suspend-all.  (We may briefly
 * suspend when the GC thread calls dvmUnlockHeap before dvmResumeAllThreads,
 * but there's no risk of deadlock.)
 */
bool dvmWaitForConcurrentGcToComplete()
{
    bool waited = gDvm.gcHeap->gcRunning;
    Thread *self = dvmThreadSelf();
    assert(self != NULL);
#ifdef DEBUG
    u4 start = dvmGetRelativeTimeMsec();
#endif
    while (gDvm.gcHeap->gcRunning) {
        ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
        dvmWaitCond(&gDvm.gcHeapCond, &gDvm.gcHeapLock);
        dvmChangeStatus(self, oldStatus);
    }
#ifdef DEBUG
    u4 end = dvmGetRelativeTimeMsec();
    if (end - start > 0) {
        ALOGD("WAIT_FOR_CONCURRENT_GC blocked %ums", end - start);
    }
#endif
    return waited;
}
コード例 #12
0
/*
 * The garbage collection daemon.  Initiates a concurrent collection
 * when signaled.  Also periodically trims the heaps when a few seconds
 * have elapsed since the last concurrent GC.
 */
static void *gcDaemonThread(void* arg)
{
    dvmChangeStatus(NULL, THREAD_VMWAIT);
    dvmLockMutex(&gHs->gcThreadMutex);
    while (gHs->gcThreadShutdown != true) {
        bool trim = false;
        if (gHs->gcThreadTrimNeeded) {
            int result = dvmRelativeCondWait(&gHs->gcThreadCond, &gHs->gcThreadMutex,
                                             HEAP_TRIM_IDLE_TIME_MS, 0);
            if (result == ETIMEDOUT) {
                /* Timed out waiting for a GC request, schedule a heap trim. */
                trim = true;
            }
        } else {
            dvmWaitCond(&gHs->gcThreadCond, &gHs->gcThreadMutex);
        }

        dvmLockHeap();
        /*
         * Another thread may have started a concurrent garbage
         * collection before we were scheduled.  Check for this
         * condition before proceeding.
         */
        if (!gDvm.gcHeap->gcRunning) {
            dvmChangeStatus(NULL, THREAD_RUNNING);
            if (trim) {
                trimHeaps();
                gHs->gcThreadTrimNeeded = false;
            } else {
                dvmCollectGarbageInternal(GC_CONCURRENT);
                gHs->gcThreadTrimNeeded = true;
            }
            dvmChangeStatus(NULL, THREAD_VMWAIT);
        }
        dvmUnlockHeap();
    }
    dvmChangeStatus(NULL, THREAD_RUNNING);
    return NULL;
}
コード例 #13
0
ファイル: dexspy.cpp プロジェクト: MiCode/Xposed
// simplified copy of Method.invokeNative, but calls the original (non-hooked) method and has no access checks
// used when a method has been hooked
static jobject miui_dexspy_DexspyInstaller_invokeOriginalMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethod,
            jobjectArray params1, jclass returnType1, jobject thisObject1, jobjectArray args1) {
    // try to find the original method
    Method* method = (Method*)env->FromReflectedMethod(reflectedMethod);
    OriginalMethodsIt original = findOriginalMethod(method);
    if (original != dexspyOriginalMethods.end()) {
        method = &(*original);
    }

    // dereference parameters
    ::Thread* self = dvmThreadSelf();
    Object* thisObject = dvmDecodeIndirectRef(self, thisObject1);
    ArrayObject* args = (ArrayObject*)dvmDecodeIndirectRef(self, args1);
    ArrayObject* params = (ArrayObject*)dvmDecodeIndirectRef(self, params1);
    ClassObject* returnType = (ClassObject*)dvmDecodeIndirectRef(self, returnType1);

    // invoke the method
    dvmChangeStatus(self, THREAD_RUNNING);
    Object* result = dvmInvokeMethod(thisObject, method, args, params, returnType, true);
    dvmChangeStatus(self, THREAD_NATIVE);

    return dexspyAddLocalReference(self, result);
}
コード例 #14
0
ファイル: Sync.cpp プロジェクト: 1mobilesuper/platform_dalvik
/*
 * Wait on a monitor until timeout, interrupt, or notification.  Used for
 * Object.wait() and (somewhat indirectly) Thread.sleep() and Thread.join().
 *
 * If another thread calls Thread.interrupt(), we throw InterruptedException
 * and return immediately if one of the following are true:
 *  - blocked in wait(), wait(long), or wait(long, int) methods of Object
 *  - blocked in join(), join(long), or join(long, int) methods of Thread
 *  - blocked in sleep(long), or sleep(long, int) methods of Thread
 * Otherwise, we set the "interrupted" flag.
 *
 * Checks to make sure that "nsec" is in the range 0-999999
 * (i.e. fractions of a millisecond) and throws the appropriate
 * exception if it isn't.
 *
 * The spec allows "spurious wakeups", and recommends that all code using
 * Object.wait() do so in a loop.  This appears to derive from concerns
 * about pthread_cond_wait() on multiprocessor systems.  Some commentary
 * on the web casts doubt on whether these can/should occur.
 *
 * Since we're allowed to wake up "early", we clamp extremely long durations
 * to return at the end of the 32-bit time epoch.
 */
static void waitMonitor(Thread* self, Monitor* mon, s8 msec, s4 nsec,
    bool interruptShouldThrow)
{
    struct timespec ts;
    bool wasInterrupted = false;
    bool timed;
    int ret;

    assert(self != NULL);
    assert(mon != NULL);

    /* Make sure that we hold the lock. */
    if (mon->owner != self) {
        dvmThrowIllegalMonitorStateException(
            "object not locked by thread before wait()");
        return;
    }

    /*
     * Enforce the timeout range.
     */
    if (msec < 0 || nsec < 0 || nsec > 999999) {
        dvmThrowIllegalArgumentException("timeout arguments out of range");
        return;
    }

    /*
     * Compute absolute wakeup time, if necessary.
     */
    if (msec == 0 && nsec == 0) {
        timed = false;
    } else {
        absoluteTime(msec, nsec, &ts);
        timed = true;
    }

    /*
     * Add ourselves to the set of threads waiting on this monitor, and
     * release our hold.  We need to let it go even if we're a few levels
     * deep in a recursive lock, and we need to restore that later.
     *
     * We append to the wait set ahead of clearing the count and owner
     * fields so the subroutine can check that the calling thread owns
     * the monitor.  Aside from that, the order of member updates is
     * not order sensitive as we hold the pthread mutex.
     */
    waitSetAppend(mon, self);
    int prevLockCount = mon->lockCount;
    mon->lockCount = 0;
    mon->owner = NULL;

    const Method* savedMethod = mon->ownerMethod;
    u4 savedPc = mon->ownerPc;
    mon->ownerMethod = NULL;
    mon->ownerPc = 0;

    /*
     * Update thread status.  If the GC wakes up, it'll ignore us, knowing
     * that we won't touch any references in this state, and we'll check
     * our suspend mode before we transition out.
     */
    if (timed)
        dvmChangeStatus(self, THREAD_TIMED_WAIT);
    else
        dvmChangeStatus(self, THREAD_WAIT);

    dvmLockMutex(&self->waitMutex);

    /*
     * Set waitMonitor to the monitor object we will be waiting on.
     * When waitMonitor is non-NULL a notifying or interrupting thread
     * must signal the thread's waitCond to wake it up.
     */
    assert(self->waitMonitor == NULL);
    self->waitMonitor = mon;

    /*
     * Handle the case where the thread was interrupted before we called
     * wait().
     */
    if (self->interrupted) {
        wasInterrupted = true;
        self->waitMonitor = NULL;
        dvmUnlockMutex(&self->waitMutex);
        goto done;
    }

    /*
     * Release the monitor lock and wait for a notification or
     * a timeout to occur.
     */
    dvmUnlockMutex(&mon->lock);

    if (!timed) {
        ret = pthread_cond_wait(&self->waitCond, &self->waitMutex);
        assert(ret == 0);
    } else {
#ifdef HAVE_TIMEDWAIT_MONOTONIC
        ret = pthread_cond_timedwait_monotonic(&self->waitCond, &self->waitMutex, &ts);
#else
        ret = pthread_cond_timedwait(&self->waitCond, &self->waitMutex, &ts);
#endif
        assert(ret == 0 || ret == ETIMEDOUT);
    }
    if (self->interrupted) {
        wasInterrupted = true;
    }

    self->interrupted = false;
    self->waitMonitor = NULL;

    dvmUnlockMutex(&self->waitMutex);

    /* Reacquire the monitor lock. */
    lockMonitor(self, mon);

done:
    /*
     * We remove our thread from wait set after restoring the count
     * and owner fields so the subroutine can check that the calling
     * thread owns the monitor. Aside from that, the order of member
     * updates is not order sensitive as we hold the pthread mutex.
     */
    mon->owner = self;
    mon->lockCount = prevLockCount;
    mon->ownerMethod = savedMethod;
    mon->ownerPc = savedPc;
    waitSetRemove(mon, self);

    /* set self->status back to THREAD_RUNNING, and self-suspend if needed */
    dvmChangeStatus(self, THREAD_RUNNING);

    if (wasInterrupted) {
        /*
         * We were interrupted while waiting, or somebody interrupted an
         * un-interruptible thread earlier and we're bailing out immediately.
         *
         * The doc sayeth: "The interrupted status of the current thread is
         * cleared when this exception is thrown."
         */
        self->interrupted = false;
        if (interruptShouldThrow) {
            dvmThrowInterruptedException(NULL);
        }
    }
}
コード例 #15
0
/*
 * The heap worker thread sits quietly until the GC tells it there's work
 * to do.
 */
static void* heapWorkerThreadStart(void* arg)
{
    Thread *self = dvmThreadSelf();
    int cc;

    UNUSED_PARAMETER(arg);

    LOGV("HeapWorker thread started (threadid=%d)\n", self->threadId);

    /* tell the main thread that we're ready */
    dvmLockMutex(&gDvm.heapWorkerLock);
    gDvm.heapWorkerReady = true;
    cc = pthread_cond_signal(&gDvm.heapWorkerCond);
    assert(cc == 0);
    dvmUnlockMutex(&gDvm.heapWorkerLock);

    dvmLockMutex(&gDvm.heapWorkerLock);
    while (!gDvm.haltHeapWorker) {
        struct timespec trimtime;
        bool timedwait = false;

        /* We're done running interpreted code for now. */
        dvmChangeStatus(NULL, THREAD_VMWAIT);

        /* Signal anyone who wants to know when we're done. */
        cc = pthread_cond_broadcast(&gDvm.heapWorkerIdleCond);
        assert(cc == 0);

        /* Trim the heap if we were asked to. */
        trimtime = gDvm.gcHeap->heapWorkerNextTrim;
        if (trimtime.tv_sec != 0 && trimtime.tv_nsec != 0) {
            struct timespec now;

#ifdef HAVE_TIMEDWAIT_MONOTONIC
            clock_gettime(CLOCK_MONOTONIC, &now);       // relative time
#else
            struct timeval tvnow;
            gettimeofday(&tvnow, NULL);                 // absolute time
            now.tv_sec = tvnow.tv_sec;
            now.tv_nsec = tvnow.tv_usec * 1000;
#endif

            if (trimtime.tv_sec < now.tv_sec ||
                (trimtime.tv_sec == now.tv_sec && 
                 trimtime.tv_nsec <= now.tv_nsec))
            {
                size_t madvisedSizes[HEAP_SOURCE_MAX_HEAP_COUNT];

                /* The heap must be locked before the HeapWorker;
                 * unroll and re-order the locks.  dvmLockHeap()
                 * will put us in VMWAIT if necessary.  Once it
                 * returns, there shouldn't be any contention on
                 * heapWorkerLock.
                 */
                dvmUnlockMutex(&gDvm.heapWorkerLock);
                dvmLockHeap();
                dvmLockMutex(&gDvm.heapWorkerLock);

                memset(madvisedSizes, 0, sizeof(madvisedSizes));
                dvmHeapSourceTrim(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT);
                dvmLogMadviseStats(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT);

                dvmUnlockHeap();

                trimtime.tv_sec = 0;
                trimtime.tv_nsec = 0;
                gDvm.gcHeap->heapWorkerNextTrim = trimtime;
            } else {
                timedwait = true;
            }
        }

        /* sleep until signaled */
        if (timedwait) {
#ifdef HAVE_TIMEDWAIT_MONOTONIC
            cc = pthread_cond_timedwait_monotonic(&gDvm.heapWorkerCond,
                    &gDvm.heapWorkerLock, &trimtime);
#else
            cc = pthread_cond_timedwait(&gDvm.heapWorkerCond,
                    &gDvm.heapWorkerLock, &trimtime);
#endif
            assert(cc == 0 || cc == ETIMEDOUT || cc == EINTR);
        } else {
            cc = pthread_cond_wait(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock);
            assert(cc == 0);
        }

        /* dvmChangeStatus() may block;  don't hold heapWorkerLock.
         */
        dvmUnlockMutex(&gDvm.heapWorkerLock);
        dvmChangeStatus(NULL, THREAD_RUNNING);
        dvmLockMutex(&gDvm.heapWorkerLock);
        LOGV("HeapWorker is awake\n");

        /* Process any events in the queue.
         */
        doHeapWork(self);
    }
    dvmUnlockMutex(&gDvm.heapWorkerLock);

    if (gDvm.verboseShutdown)
        LOGD("HeapWorker thread shutting down\n");
    return NULL;
}
コード例 #16
0
ファイル: dexspy.cpp プロジェクト: MiCode/Xposed
static void dexspyCallHandler(const u4* args, JValue* pResult, const Method* method, ::Thread* self) {
    OriginalMethodsIt original = findOriginalMethod(method);
    if (original == dexspyOriginalMethods.end()) {
        dvmThrowNoSuchMethodError("could not find Dexspy original method - how did you even get here?");
        return;
    }

    ThreadStatus oldThreadStatus = self->status;
    JNIEnv* env = self->jniEnv;

    // get java.lang.reflect.Method object for original method
    jobject originalReflected = env->ToReflectedMethod(
        (jclass)dexspyAddLocalReference(self, original->clazz),
        (jmethodID)method,
        true);

    // convert/box arguments
    const char* desc = &method->shorty[1]; // [0] is the return type.
    Object* thisObject = NULL;
    size_t srcIndex = 0;
    size_t dstIndex = 0;

    // for non-static methods determine the "this" pointer
    if (!dvmIsStaticMethod(&(*original))) {
        thisObject = (Object*) dexspyAddLocalReference(self, (Object*)args[0]);
        srcIndex++;
    }

    jclass objectClass = env->FindClass("java/lang/Object");
    jobjectArray argsArray = env->NewObjectArray(strlen(method->shorty) - 1, objectClass, NULL);

    while (*desc != '\0') {
        char descChar = *(desc++);
        JValue value;
        Object* obj;

        switch (descChar) {
        case 'Z':
        case 'C':
        case 'F':
        case 'B':
        case 'S':
        case 'I':
            value.i = args[srcIndex++];
            obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar));
            dvmReleaseTrackedAlloc(obj, NULL);
            break;
        case 'D':
        case 'J':
            value.j = dvmGetArgLong(args, srcIndex);
            srcIndex += 2;
            obj = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar));
            dvmReleaseTrackedAlloc(obj, NULL);
            break;
        case '[':
        case 'L':
            obj  = (Object*) args[srcIndex++];
            break;
        default:
            ALOGE("Unknown method signature description character: %c\n", descChar);
            obj = NULL;
            srcIndex++;
        }
        env->SetObjectArrayElement(argsArray, dstIndex++, dexspyAddLocalReference(self, obj));
    }

    // call the Java handler function
    jobject resultRef = env->CallStaticObjectMethod(
        dexspyClass, dexspyHandleHookedMethod, originalReflected, thisObject, argsArray);

    // exceptions are thrown to the caller
    if (env->ExceptionCheck()) {
        dvmChangeStatus(self, oldThreadStatus);
        return;
    }

    // return result with proper type
    Object* result = dvmDecodeIndirectRef(self, resultRef);
    ClassObject* returnType = dvmGetBoxedReturnType(method);
    if (returnType->primitiveType == PRIM_VOID) {
        // ignored
    } else if (result == NULL) {
        if (dvmIsPrimitiveClass(returnType)) {
            dvmThrowNullPointerException("null result when primitive expected");
        }
        pResult->l = NULL;
    } else {
        if (!dvmUnboxPrimitive(result, returnType, pResult)) {
            dvmThrowClassCastException(result->clazz, returnType);
        }
    }

    // set the thread status back to running. must be done after the last env->...()
    dvmChangeStatus(self, oldThreadStatus);
}
コード例 #17
0
ファイル: DexPrepare.c プロジェクト: ThoreTechnics/in-the-box
/*
 * Given a descriptor for a file with DEX data in it, produce an
 * optimized version.
 *
 * The file pointed to by "fd" is expected to be a locked shared resource
 * (or private); we make no efforts to enforce multi-process correctness
 * here.
 *
 * "fileName" is only used for debug output.  "modWhen" and "crc" are stored
 * in the dependency set.
 *
 * The "isBootstrap" flag determines how the optimizer and verifier handle
 * package-scope access checks.  When optimizing, we only load the bootstrap
 * class DEX files and the target DEX, so the flag determines whether the
 * target DEX classes are given a (synthetic) non-NULL classLoader pointer.
 * This only really matters if the target DEX contains classes that claim to
 * be in the same package as bootstrap classes.
 *
 * The optimizer will need to load every class in the target DEX file.
 * This is generally undesirable, so we start a subprocess to do the
 * work and wait for it to complete.
 *
 * Returns "true" on success.  All data will have been written to "fd".
 */
bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
    const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
{
    const char* lastPart = strrchr(fileName, '/');
    if (lastPart != NULL)
        lastPart++;
    else
        lastPart = fileName;

    LOGD("DexOpt: --- BEGIN '%s' (bootstrap=%d) ---\n", lastPart, isBootstrap);

    pid_t pid;

    /*
     * This could happen if something in our bootclasspath, which we thought
     * was all optimized, got rejected.
     */
    if (gDvm.optimizing) {
        LOGW("Rejecting recursive optimization attempt on '%s'\n", fileName);
        return false;
    }

    pid = fork();
    if (pid == 0) {
        static const int kUseValgrind = 0;
        static const char* kDexOptBin = "/bin/dexopt";
        static const char* kValgrinder = "/usr/bin/valgrind";
        static const int kFixedArgCount = 10;
        static const int kValgrindArgCount = 5;
        static const int kMaxIntLen = 12;   // '-'+10dig+'\0' -OR- 0x+8dig
        int bcpSize = dvmGetBootPathSize();
        int argc = kFixedArgCount + bcpSize
            + (kValgrindArgCount * kUseValgrind);
        char* argv[argc+1];             // last entry is NULL
        char values[argc][kMaxIntLen];
        char* execFile;
        char* androidRoot;
        int flags;

        /* change process groups, so we don't clash with ProcessManager */
        setpgid(0, 0);

        /* full path to optimizer */
        androidRoot = getenv("ANDROID_ROOT");
        if (androidRoot == NULL) {
            LOGW("ANDROID_ROOT not set, defaulting to /system\n");
            androidRoot = "/system";
        }
        execFile = malloc(strlen(androidRoot) + strlen(kDexOptBin) + 1);
        strcpy(execFile, androidRoot);
        strcat(execFile, kDexOptBin);

        /*
         * Create arg vector.
         */
        int curArg = 0;

        if (kUseValgrind) {
            /* probably shouldn't ship the hard-coded path */
            argv[curArg++] = (char*)kValgrinder;
            argv[curArg++] = "--tool=memcheck";
            argv[curArg++] = "--leak-check=yes";        // check for leaks too
            argv[curArg++] = "--leak-resolution=med";   // increase from 2 to 4
            argv[curArg++] = "--num-callers=16";        // default is 12
            assert(curArg == kValgrindArgCount);
        }
        argv[curArg++] = execFile;

        argv[curArg++] = "--dex";

        sprintf(values[2], "%d", DALVIK_VM_BUILD);
        argv[curArg++] = values[2];

        sprintf(values[3], "%d", fd);
        argv[curArg++] = values[3];

        sprintf(values[4], "%d", (int) dexOffset);
        argv[curArg++] = values[4];

        sprintf(values[5], "%d", (int) dexLength);
        argv[curArg++] = values[5];

        argv[curArg++] = (char*)fileName;

        sprintf(values[7], "%d", (int) modWhen);
        argv[curArg++] = values[7];

        sprintf(values[8], "%d", (int) crc);
        argv[curArg++] = values[8];

        flags = 0;
        if (gDvm.dexOptMode != OPTIMIZE_MODE_NONE) {
            flags |= DEXOPT_OPT_ENABLED;
            if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)
                flags |= DEXOPT_OPT_ALL;
        }
        if (gDvm.classVerifyMode != VERIFY_MODE_NONE) {
            flags |= DEXOPT_VERIFY_ENABLED;
            if (gDvm.classVerifyMode == VERIFY_MODE_ALL)
                flags |= DEXOPT_VERIFY_ALL;
        }
        if (isBootstrap)
            flags |= DEXOPT_IS_BOOTSTRAP;
        if (gDvm.generateRegisterMaps)
            flags |= DEXOPT_GEN_REGISTER_MAPS;
        sprintf(values[9], "%d", flags);
        argv[curArg++] = values[9];

        assert(((!kUseValgrind && curArg == kFixedArgCount) ||
               ((kUseValgrind && curArg == kFixedArgCount+kValgrindArgCount))));

        ClassPathEntry* cpe;
        for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) {
            argv[curArg++] = cpe->fileName;
        }
        assert(curArg == argc);

        argv[curArg] = NULL;

        if (kUseValgrind)
            execv(kValgrinder, argv);
        else
            execv(execFile, argv);

        LOGE("execv '%s'%s failed: %s\n", execFile,
            kUseValgrind ? " [valgrind]" : "", strerror(errno));
        exit(1);
    } else {
        LOGV("DexOpt: waiting for verify+opt, pid=%d\n", (int) pid);
        int status;
        pid_t gotPid;
        int oldStatus;

        /*
         * Wait for the optimization process to finish.  We go into VMWAIT
         * mode here so GC suspension won't have to wait for us.
         */
        oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
        while (true) {
            gotPid = waitpid(pid, &status, 0);
            if (gotPid == -1 && errno == EINTR) {
                LOGD("waitpid interrupted, retrying\n");
            } else {
                break;
            }
        }
        dvmChangeStatus(NULL, oldStatus);
        if (gotPid != pid) {
            LOGE("waitpid failed: wanted %d, got %d: %s\n",
                (int) pid, (int) gotPid, strerror(errno));
            return false;
        }

        if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
            LOGD("DexOpt: --- END '%s' (success) ---\n", lastPart);
            return true;
        } else {
            LOGW("DexOpt: --- END '%s' --- status=0x%04x, process failed\n",
                lastPart, status);
            return false;
        }
    }
}
コード例 #18
0
/*
 * The heap worker thread sits quietly until the GC tells it there's work
 * to do.
 */
static void* heapWorkerThreadStart(void* arg)
{
    Thread *self = dvmThreadSelf();

    UNUSED_PARAMETER(arg);

    LOGV("HeapWorker thread started (threadid=%d)\n", self->threadId);

    /* tell the main thread that we're ready */
    lockMutex(&gDvm.heapWorkerLock);
    gDvm.heapWorkerReady = true;
    dvmSignalCond(&gDvm.heapWorkerCond);
    dvmUnlockMutex(&gDvm.heapWorkerLock);

    lockMutex(&gDvm.heapWorkerLock);
    while (!gDvm.haltHeapWorker) {
        struct timespec trimtime;
        bool timedwait = false;

        /* We're done running interpreted code for now. */
        dvmChangeStatus(NULL, THREAD_VMWAIT);

        /* Signal anyone who wants to know when we're done. */
        dvmBroadcastCond(&gDvm.heapWorkerIdleCond);

        /* Trim the heap if we were asked to. */
        trimtime = gDvm.gcHeap->heapWorkerNextTrim;
        if (trimtime.tv_sec != 0 && trimtime.tv_nsec != 0) {
            struct timespec now;

#ifdef HAVE_TIMEDWAIT_MONOTONIC
            clock_gettime(CLOCK_MONOTONIC, &now);       // relative time
#else
            struct timeval tvnow;
            gettimeofday(&tvnow, NULL);                 // absolute time
            now.tv_sec = tvnow.tv_sec;
            now.tv_nsec = tvnow.tv_usec * 1000;
#endif

            if (trimtime.tv_sec < now.tv_sec ||
                (trimtime.tv_sec == now.tv_sec &&
                 trimtime.tv_nsec <= now.tv_nsec))
            {
                size_t madvisedSizes[HEAP_SOURCE_MAX_HEAP_COUNT];

                /*
                 * Acquire the gcHeapLock.  The requires releasing the
                 * heapWorkerLock before the gcHeapLock is acquired.
                 * It is possible that the gcHeapLock may be acquired
                 * during a concurrent GC in which case heapWorkerLock
                 * is held by the GC and we are unable to make forward
                 * progress.  We avoid deadlock by releasing the
                 * gcHeapLock and then waiting to be signaled when the
                 * GC completes.  There is no guarantee that the next
                 * time we are run will coincide with GC inactivity so
                 * the check and wait must be performed within a loop.
                 */
                dvmUnlockMutex(&gDvm.heapWorkerLock);
                dvmLockHeap();
                while (gDvm.gcHeap->gcRunning) {
                    dvmWaitForConcurrentGcToComplete();
                }
                dvmLockMutex(&gDvm.heapWorkerLock);

                memset(madvisedSizes, 0, sizeof(madvisedSizes));
                dvmHeapSourceTrim(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT);
                dvmLogMadviseStats(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT);

                dvmUnlockHeap();

                trimtime.tv_sec = 0;
                trimtime.tv_nsec = 0;
                gDvm.gcHeap->heapWorkerNextTrim = trimtime;
            } else {
                timedwait = true;
            }
        }

        /* sleep until signaled */
        if (timedwait) {
            int cc __attribute__ ((__unused__));
#ifdef HAVE_TIMEDWAIT_MONOTONIC
            cc = pthread_cond_timedwait_monotonic(&gDvm.heapWorkerCond,
                    &gDvm.heapWorkerLock, &trimtime);
#else
            cc = pthread_cond_timedwait(&gDvm.heapWorkerCond,
                    &gDvm.heapWorkerLock, &trimtime);
#endif
            assert(cc == 0 || cc == ETIMEDOUT);
        } else {
            dvmWaitCond(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock);
        }

        /*
         * Return to the running state before doing heap work.  This
         * will block if the GC has initiated a suspend.  We release
         * the heapWorkerLock beforehand for the GC to make progress
         * and wait to be signaled after the GC completes.  There is
         * no guarantee that the next time we are run will coincide
         * with GC inactivity so the check and wait must be performed
         * within a loop.
         */
        dvmUnlockMutex(&gDvm.heapWorkerLock);
        dvmChangeStatus(NULL, THREAD_RUNNING);
        dvmLockHeap();
        while (gDvm.gcHeap->gcRunning) {
            dvmWaitForConcurrentGcToComplete();
        }
        dvmLockMutex(&gDvm.heapWorkerLock);
        dvmUnlockHeap();
        LOGV("HeapWorker is awake\n");

        /* Process any events in the queue.
         */
        doHeapWork(self);
    }
    dvmUnlockMutex(&gDvm.heapWorkerLock);

    if (gDvm.verboseShutdown)
        LOGD("HeapWorker thread shutting down\n");
    return NULL;
}
コード例 #19
0
ファイル: StdioConverter.cpp プロジェクト: XClouded/fastiva
/*
 * Select on stdout/stderr pipes, waiting for activity.
 *
 * DO NOT use printf from here.
 */
static void* stdioConverterThreadStart(void* arg)
{
    int cc;
#ifdef _WIN32
	_asm int 3;
#else

    /* tell the main thread that we're ready */
    dvmLockMutex(&gDvm.stdioConverterLock);
    gDvm.stdioConverterReady = true;
    cc = pthread_cond_signal(&gDvm.stdioConverterCond);
    assert(cc == 0);
    dvmUnlockMutex(&gDvm.stdioConverterLock);

    /* we never do anything that affects the rest of the VM */
    dvmChangeStatus(NULL, THREAD_VMWAIT);

    /*
     * Allocate read buffers.
     */
    BufferedData* stdoutData = new BufferedData;
    BufferedData* stderrData = new BufferedData;
    stdoutData->count = stderrData->count = 0;

    /*
     * Read until shutdown time.
     */
    while (!gDvm.haltStdioConverter) {
        fd_set readfds;
        int maxFd, fdCount;

        FD_ZERO(&readfds);
        FD_SET(gDvm.stdoutPipe[0], &readfds);
        FD_SET(gDvm.stderrPipe[0], &readfds);
        maxFd = MAX(gDvm.stdoutPipe[0], gDvm.stderrPipe[0]);

        fdCount = select(maxFd+1, &readfds, NULL, NULL, NULL);

        if (fdCount < 0) {
            if (errno != EINTR) {
                ALOGE("select on stdout/stderr failed");
                break;
            }
            ALOGD("Got EINTR, ignoring");
        } else if (fdCount == 0) {
            ALOGD("WEIRD: select returned zero");
        } else {
            bool err = false;
            if (FD_ISSET(gDvm.stdoutPipe[0], &readfds)) {
                err |= !readAndLog(gDvm.stdoutPipe[0], stdoutData,
                    "stdout");
            }
            if (FD_ISSET(gDvm.stderrPipe[0], &readfds)) {
                err |= !readAndLog(gDvm.stderrPipe[0], stderrData,
                    "stderr");
            }

            /* probably EOF; give up */
            if (err) {
                ALOGW("stdio converter got read error; shutting it down");
                break;
            }
        }
    }

    close(gDvm.stdoutPipe[0]);
    close(gDvm.stderrPipe[0]);

    delete stdoutData;
    delete stderrData;

    /* change back for shutdown sequence */
    dvmChangeStatus(NULL, THREAD_RUNNING);
#endif
    return NULL;
}
コード例 #20
0
ファイル: DexPrepare.c プロジェクト: ThoreTechnics/in-the-box
/*
 * Return the fd of an open file in the DEX file cache area.  If the cache
 * file doesn't exist or is out of date, this will remove the old entry,
 * create a new one (writing only the file header), and return with the
 * "new file" flag set.
 *
 * It's possible to execute from an unoptimized DEX file directly,
 * assuming the byte ordering and structure alignment is correct, but
 * disadvantageous because some significant optimizations are not possible.
 * It's not generally possible to do the same from an uncompressed Jar
 * file entry, because we have to guarantee 32-bit alignment in the
 * memory-mapped file.
 *
 * For a Jar/APK file (a zip archive with "classes.dex" inside), "modWhen"
 * and "crc32" come from the Zip directory entry.  For a stand-alone DEX
 * file, it's the modification date of the file and the Adler32 from the
 * DEX header (which immediately follows the magic).  If these don't
 * match what's stored in the opt header, we reject the file immediately.
 *
 * On success, the file descriptor will be positioned just past the "opt"
 * file header, and will be locked with flock.  "*pCachedName" will point
 * to newly-allocated storage.
 */
int dvmOpenCachedDexFile(const char* fileName, const char* cacheFileName,
    u4 modWhen, u4 crc, bool isBootstrap, bool* pNewFile, bool createIfMissing)
{
    int fd, cc;
    struct stat fdStat, fileStat;
    bool readOnly = false;

    *pNewFile = false;

retry:
    /*
     * Try to open the cache file.  If we've been asked to,
     * create it if it doesn't exist.
     */
    fd = createIfMissing ? open(cacheFileName, O_CREAT|O_RDWR, 0644) : -1;
    if (fd < 0) {
        fd = open(cacheFileName, O_RDONLY, 0);
        if (fd < 0) {
            if (createIfMissing) {
                LOGE("Can't open dex cache '%s': %s\n",
                    cacheFileName, strerror(errno));
            }
            return fd;
        }
        readOnly = true;
    }

    /*
     * Grab an exclusive lock on the cache file.  If somebody else is
     * working on it, we'll block here until they complete.  Because
     * we're waiting on an external resource, we go into VMWAIT mode.
     */
    int oldStatus;
    LOGV("DexOpt: locking cache file %s (fd=%d, boot=%d)\n",
        cacheFileName, fd, isBootstrap);
    oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
    cc = flock(fd, LOCK_EX | LOCK_NB);
    if (cc != 0) {
        LOGD("DexOpt: sleeping on flock(%s)\n", cacheFileName);
        cc = flock(fd, LOCK_EX);
    }
    dvmChangeStatus(NULL, oldStatus);
    if (cc != 0) {
        LOGE("Can't lock dex cache '%s': %d\n", cacheFileName, cc);
        close(fd);
        return -1;
    }
    LOGV("DexOpt:  locked cache file\n");

    /*
     * Check to see if the fd we opened and locked matches the file in
     * the filesystem.  If they don't, then somebody else unlinked ours
     * and created a new file, and we need to use that one instead.  (If
     * we caught them between the unlink and the create, we'll get an
     * ENOENT from the file stat.)
     */
    cc = fstat(fd, &fdStat);
    if (cc != 0) {
        LOGE("Can't stat open file '%s'\n", cacheFileName);
        LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
        goto close_fail;
    }
    cc = stat(cacheFileName, &fileStat);
    if (cc != 0 ||
        fdStat.st_dev != fileStat.st_dev || fdStat.st_ino != fileStat.st_ino)
    {
        LOGD("DexOpt: our open cache file is stale; sleeping and retrying\n");
        LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
        flock(fd, LOCK_UN);
        close(fd);
        usleep(250 * 1000);     /* if something is hosed, don't peg machine */
        goto retry;
    }

    /*
     * We have the correct file open and locked.  If the file size is zero,
     * then it was just created by us, and we want to fill in some fields
     * in the "opt" header and set "*pNewFile".  Otherwise, we want to
     * verify that the fields in the header match our expectations, and
     * reset the file if they don't.
     */
    if (fdStat.st_size == 0) {
        if (readOnly) {
            LOGW("DexOpt: file has zero length and isn't writable\n");
            goto close_fail;
        }
        cc = dexOptCreateEmptyHeader(fd);
        if (cc != 0)
            goto close_fail;
        *pNewFile = true;
        LOGV("DexOpt: successfully initialized new cache file\n");
    } else {
        bool expectVerify, expectOpt;

        if (gDvm.classVerifyMode == VERIFY_MODE_NONE)
            expectVerify = false;
        else if (gDvm.classVerifyMode == VERIFY_MODE_REMOTE)
            expectVerify = !isBootstrap;
        else /*if (gDvm.classVerifyMode == VERIFY_MODE_ALL)*/
            expectVerify = true;

        if (gDvm.dexOptMode == OPTIMIZE_MODE_NONE)
            expectOpt = false;
        else if (gDvm.dexOptMode == OPTIMIZE_MODE_VERIFIED)
            expectOpt = expectVerify;
        else /*if (gDvm.dexOptMode == OPTIMIZE_MODE_ALL)*/
            expectOpt = true;

        LOGV("checking deps, expecting vfy=%d opt=%d\n",
            expectVerify, expectOpt);

        if (!dvmCheckOptHeaderAndDependencies(fd, true, modWhen, crc,
                expectVerify, expectOpt))
        {
            if (readOnly) {
                /*
                 * We could unlink and rewrite the file if we own it or
                 * the "sticky" bit isn't set on the directory.  However,
                 * we're not able to truncate it, which spoils things.  So,
                 * give up now.
                 */
                if (createIfMissing) {
                    LOGW("Cached DEX '%s' (%s) is stale and not writable\n",
                        fileName, cacheFileName);
                }
                goto close_fail;
            }

            /*
             * If we truncate the existing file before unlinking it, any
             * process that has it mapped will fail when it tries to touch
             * the pages.
             *
             * This is very important.  The zygote process will have the
             * boot DEX files (core, framework, etc.) mapped early.  If
             * (say) core.dex gets updated, and somebody launches an app
             * that uses App.dex, then App.dex gets reoptimized because it's
             * dependent upon the boot classes.  However, dexopt will be
             * using the *new* core.dex to do the optimizations, while the
             * app will actually be running against the *old* core.dex
             * because it starts from zygote.
             *
             * Even without zygote, it's still possible for a class loader
             * to pull in an APK that was optimized against an older set
             * of DEX files.  We must ensure that everything fails when a
             * boot DEX gets updated, and for general "why aren't my
             * changes doing anything" purposes its best if we just make
             * everything crash when a DEX they're using gets updated.
             */
            LOGD("ODEX file is stale or bad; removing and retrying (%s)\n",
                cacheFileName);
            if (ftruncate(fd, 0) != 0) {
                LOGW("Warning: unable to truncate cache file '%s': %s\n",
                    cacheFileName, strerror(errno));
                /* keep going */
            }
            if (unlink(cacheFileName) != 0) {
                LOGW("Warning: unable to remove cache file '%s': %d %s\n",
                    cacheFileName, errno, strerror(errno));
                /* keep going; permission failure should probably be fatal */
            }
            LOGVV("DexOpt: unlocking cache file %s\n", cacheFileName);
            flock(fd, LOCK_UN);
            close(fd);
            goto retry;
        } else {
            LOGV("DexOpt: good deps in cache file\n");
        }
    }

    assert(fd >= 0);
    return fd;

close_fail:
    flock(fd, LOCK_UN);
    close(fd);
    return -1;
}
コード例 #21
0
/*
 * Sleep in sigwait() until a signal arrives.
 */
static void* signalCatcherThreadStart(void* arg)
{
    Thread* self = dvmThreadSelf();
    sigset_t mask;
    int cc;

    UNUSED_PARAMETER(arg);

    ALOGV("Signal catcher thread started (threadid=%d)", self->threadId);

    /* set up mask with signals we want to handle */
    sigemptyset(&mask);
    sigaddset(&mask, SIGQUIT);
    sigaddset(&mask, SIGUSR1);
#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
    sigaddset(&mask, SIGUSR2);
#endif

    while (true) {
        int rcvd;

        dvmChangeStatus(self, THREAD_VMWAIT);

        /*
         * Signals for sigwait() must be blocked but not ignored.  We
         * block signals like SIGQUIT for all threads, so the condition
         * is met.  When the signal hits, we wake up, without any signal
         * handlers being invoked.
         *
         * When running under GDB we occasionally return from sigwait()
         * with EINTR (e.g. when other threads exit).
         */
loop:
        cc = sigwait(&mask, &rcvd);
        if (cc != 0) {
            if (cc == EINTR) {
                //ALOGV("sigwait: EINTR");
                goto loop;
            }
            assert(!"bad result from sigwait");
        }

        if (!gDvm.haltSignalCatcher) {
            ALOGI("threadid=%d: reacting to signal %d",
                dvmThreadSelf()->threadId, rcvd);
        }

        /* set our status to RUNNING, self-suspending if GC in progress */
        dvmChangeStatus(self, THREAD_RUNNING);

        if (gDvm.haltSignalCatcher)
            break;

        switch (rcvd) {
        case SIGQUIT:
            handleSigQuit();
            break;
        case SIGUSR1:
            handleSigUsr1();
            break;
#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
        case SIGUSR2:
            handleSigUsr2();
            break;
#endif
        default:
            ALOGE("unexpected signal %d", rcvd);
            break;
        }
    }

    return NULL;
}
コード例 #22
0
/*
 * Respond to a SIGQUIT by dumping the thread stacks.  Optionally dump
 * a few other things while we're at it.
 *
 * Thread stacks can either go to the log or to a file designated for holding
 * ANR traces.  If we're writing to a file, we want to do it in one shot,
 * so we can use a single O_APPEND write instead of contending for exclusive
 * access with flock().  There may be an advantage in resuming the VM
 * before doing the file write, so we don't stall the VM if disk I/O is
 * bottlenecked.
 *
 * If JIT tuning is compiled in, dump compiler stats as well.
 */
static void handleSigQuit()
{
    char* traceBuf = NULL;
    size_t traceLen;

    dvmSuspendAllThreads(SUSPEND_FOR_STACK_DUMP);

    dvmDumpLoaderStats("sig");

    if (gDvm.stackTraceFile == NULL) {
        /* just dump to log */
        DebugOutputTarget target;
        dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
        dvmDumpJniStats(&target);
        dvmDumpAllThreadsEx(&target, true);
    } else {
        /* write to memory buffer */
        FILE* memfp = open_memstream(&traceBuf, &traceLen);
        if (memfp == NULL) {
            ALOGE("Unable to create memstream for stack traces");
            traceBuf = NULL;        /* make sure it didn't touch this */
            /* continue on */
        } else {
            logThreadStacks(memfp);
            fclose(memfp);
        }
    }

#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
    dvmCompilerDumpStats();
#endif

    if (false) dvmDumpTrackedAllocations(true);

    dvmResumeAllThreads(SUSPEND_FOR_STACK_DUMP);

    if (traceBuf != NULL) {
        /*
         * We don't know how long it will take to do the disk I/O, so put us
         * into VMWAIT for the duration.
         */
        ThreadStatus oldStatus = dvmChangeStatus(dvmThreadSelf(), THREAD_VMWAIT);

        /*
         * Open the stack trace output file, creating it if necessary.  It
         * needs to be world-writable so other processes can write to it.
         */
        int fd = open(gDvm.stackTraceFile, O_WRONLY | O_APPEND | O_CREAT, 0666);
        if (fd < 0) {
            ALOGE("Unable to open stack trace file '%s': %s",
                gDvm.stackTraceFile, strerror(errno));
        } else {
            ssize_t actual = write(fd, traceBuf, traceLen);
            if (actual != (ssize_t) traceLen) {
                ALOGE("Failed to write stack traces to %s (%d of %zd): %s",
                    gDvm.stackTraceFile, (int) actual, traceLen,
                    strerror(errno));
            } else {
                ALOGI("Wrote stack traces to '%s'", gDvm.stackTraceFile);
            }
            close(fd);
        }

        free(traceBuf);
        dvmChangeStatus(dvmThreadSelf(), oldStatus);
    }
}
コード例 #23
0
static void *compilerThreadStart(void *arg)
{
    dvmChangeStatus(NULL, THREAD_VMWAIT);

    /*
     * If we're not running stand-alone, wait a little before
     * recieving translation requests on the assumption that process start
     * up code isn't worth compiling.  We'll resume when the framework
     * signals us that the first screen draw has happened, or the timer
     * below expires (to catch daemons).
     *
     * There is a theoretical race between the callback to
     * VMRuntime.startJitCompiation and when the compiler thread reaches this
     * point. In case the callback happens earlier, in order not to permanently
     * hold the system_server (which is not using the timed wait) in
     * interpreter-only mode we bypass the delay here.
     */
    if (gDvmJit.runningInAndroidFramework &&
        !gDvmJit.alreadyEnabledViaFramework) {
        /*
         * If the current VM instance is the system server (detected by having
         * 0 in gDvm.systemServerPid), we will use the indefinite wait on the
         * conditional variable to determine whether to start the JIT or not.
         * If the system server detects that the whole system is booted in
         * safe mode, the conditional variable will never be signaled and the
         * system server will remain in the interpreter-only mode. All
         * subsequent apps will be started with the --enable-safemode flag
         * explicitly appended.
         */
        if (gDvm.systemServerPid == 0) {
            dvmLockMutex(&gDvmJit.compilerLock);
            pthread_cond_wait(&gDvmJit.compilerQueueActivity,
                              &gDvmJit.compilerLock);
            dvmUnlockMutex(&gDvmJit.compilerLock);
            ALOGD("JIT started for system_server");
        } else {
            dvmLockMutex(&gDvmJit.compilerLock);
            /*
             * TUNING: experiment with the delay & perhaps make it
             * target-specific
             */
            dvmRelativeCondWait(&gDvmJit.compilerQueueActivity,
                                 &gDvmJit.compilerLock, 3000, 0);
            dvmUnlockMutex(&gDvmJit.compilerLock);
        }
        if (gDvmJit.haltCompilerThread) {
             return NULL;
        }
    }

    compilerThreadStartup();

    dvmLockMutex(&gDvmJit.compilerLock);
    /*
     * Since the compiler thread will not touch any objects on the heap once
     * being created, we just fake its state as VMWAIT so that it can be a
     * bit late when there is suspend request pending.
     */
    while (!gDvmJit.haltCompilerThread) {
        if (workQueueLength() == 0) {
            int cc;
            cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
            assert(cc == 0);
            pthread_cond_wait(&gDvmJit.compilerQueueActivity,
                              &gDvmJit.compilerLock);
            continue;
        } else {
            do {
                CompilerWorkOrder work = workDequeue();
                dvmUnlockMutex(&gDvmJit.compilerLock);
#if defined(WITH_JIT_TUNING)
                /*
                 * This is live across setjmp().  Mark it volatile to suppress
                 * a gcc warning.  We should not need this since it is assigned
                 * only once but gcc is not smart enough.
                 */
                volatile u8 startTime = dvmGetRelativeTimeUsec();
#endif
                /*
                 * Check whether there is a suspend request on me.  This
                 * is necessary to allow a clean shutdown.
                 *
                 * However, in the blocking stress testing mode, let the
                 * compiler thread continue doing compilations to unblock
                 * other requesting threads. This may occasionally cause
                 * shutdown from proceeding cleanly in the standalone invocation
                 * of the vm but this should be acceptable.
                 */
                if (!gDvmJit.blockingMode)
                    dvmCheckSuspendPending(dvmThreadSelf());
                /* Is JitTable filling up? */
                if (gDvmJit.jitTableEntriesUsed >
                    (gDvmJit.jitTableSize - gDvmJit.jitTableSize/4)) {
                    bool resizeFail =
                        dvmJitResizeJitTable(gDvmJit.jitTableSize * 2);
                    /*
                     * If the jit table is full, consider it's time to reset
                     * the code cache too.
                     */
                    gDvmJit.codeCacheFull |= resizeFail;
                }
                if (gDvmJit.haltCompilerThread) {
                    ALOGD("Compiler shutdown in progress - discarding request");
                } else if (!gDvmJit.codeCacheFull) {
                    jmp_buf jmpBuf;
                    work.bailPtr = &jmpBuf;
                    bool aborted = setjmp(jmpBuf);
                    if (!aborted) {
                        bool codeCompiled = dvmCompilerDoWork(&work);
                        /*
                         * Make sure we are still operating with the
                         * same translation cache version.  See
                         * Issue 4271784 for details.
                         */
                        dvmLockMutex(&gDvmJit.compilerLock);
                        if ((work.result.cacheVersion ==
                             gDvmJit.cacheVersion) &&
                             codeCompiled &&
                             !work.result.discardResult &&
                             work.result.codeAddress) {
                            dvmJitSetCodeAddr(work.pc, work.result.codeAddress,
                                              work.result.instructionSet,
                                              false, /* not method entry */
                                              work.result.profileCodeSize);
                        }
                        dvmUnlockMutex(&gDvmJit.compilerLock);
                    }
                    dvmCompilerArenaReset();
                }
                free(work.info);
#if defined(WITH_JIT_TUNING)
                gDvmJit.jitTime += dvmGetRelativeTimeUsec() - startTime;
#endif
                dvmLockMutex(&gDvmJit.compilerLock);
            } while (workQueueLength() != 0);
        }
    }
    pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
    dvmUnlockMutex(&gDvmJit.compilerLock);

    /*
     * As part of detaching the thread we need to call into Java code to update
     * the ThreadGroup, and we should not be in VMWAIT state while executing
     * interpreted code.
     */
    dvmChangeStatus(NULL, THREAD_RUNNING);

    if (gDvm.verboseShutdown)
        ALOGD("Compiler thread shutting down");
    return NULL;
}
/*
 * Sleep in sigwait() until a signal arrives.
 */
static void* signalCatcherThreadStart(void* arg)
{
    Thread* self = dvmThreadSelf();
    sigset_t mask;
    int cc;

    UNUSED_PARAMETER(arg);

    LOGV("Signal catcher thread started (threadid=%d)\n", self->threadId);

    /* set up mask with signals we want to handle */
    sigemptyset(&mask);
    sigaddset(&mask, SIGQUIT);
    sigaddset(&mask, SIGUSR1);

    while (true) {
        int rcvd;

        dvmChangeStatus(self, THREAD_VMWAIT);

        /*
         * Signals for sigwait() must be blocked but not ignored.  We
         * block signals like SIGQUIT for all threads, so the condition
         * is met.  When the signal hits, we wake up, without any signal
         * handlers being invoked.
         *
         * We want to suspend all other threads, so that it's safe to
         * traverse their stacks.
         *
         * When running under GDB we occasionally return with EINTR (e.g.
         * when other threads exit).
         */
loop:
        cc = sigwait(&mask, &rcvd);
        if (cc != 0) {
            if (cc == EINTR) {
                //LOGV("sigwait: EINTR\n");
                goto loop;
            }
            assert(!"bad result from sigwait");
        }

        if (!gDvm.haltSignalCatcher) {
            LOGI("threadid=%d: reacting to signal %d\n",
                dvmThreadSelf()->threadId, rcvd);
        }

        /* set our status to RUNNING, self-suspending if GC in progress */
        dvmChangeStatus(self, THREAD_RUNNING);

        if (gDvm.haltSignalCatcher)
            break;

        if (rcvd == SIGQUIT) {
            dvmSuspendAllThreads(SUSPEND_FOR_STACK_DUMP);
            dvmDumpLoaderStats("sig");

            logThreadStacks();

            if (false) {
                dvmLockMutex(&gDvm.jniGlobalRefLock);
                dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
                dvmUnlockMutex(&gDvm.jniGlobalRefLock);
            }

            //dvmDumpTrackedAllocations(true);
            dvmResumeAllThreads(SUSPEND_FOR_STACK_DUMP);
        } else if (rcvd == SIGUSR1) {
#if WITH_HPROF
            LOGI("SIGUSR1 forcing GC and HPROF dump\n");
            hprofDumpHeap(NULL);
#else
            LOGI("SIGUSR1 forcing GC (no HPROF)\n");
            dvmCollectGarbage(false);
#endif
        } else {
            LOGE("unexpected signal %d\n", rcvd);
        }
    }

    return NULL;
}
コード例 #25
0
ファイル: JdwpMain.c プロジェクト: AndDiSa/GB-platform_dalvik
/*
 * Initialize JDWP.
 *
 * Does not return until JDWP thread is running, but may return before
 * the thread is accepting network connections.
 */
JdwpState* dvmJdwpStartup(const JdwpStartupParams* pParams)
{
    JdwpState* state = NULL;

    /* comment this out when debugging JDWP itself */
    android_setMinPriority(LOG_TAG, ANDROID_LOG_DEBUG);

    state = (JdwpState*) calloc(1, sizeof(JdwpState));

    state->params = *pParams;

    state->requestSerial = 0x10000000;
    state->eventSerial = 0x20000000;
    dvmDbgInitMutex(&state->threadStartLock);
    dvmDbgInitMutex(&state->attachLock);
    dvmDbgInitMutex(&state->serialLock);
    dvmDbgInitMutex(&state->eventLock);
    state->eventThreadId = 0;
    dvmDbgInitMutex(&state->eventThreadLock);
    dvmDbgInitCond(&state->threadStartCond);
    dvmDbgInitCond(&state->attachCond);
    dvmDbgInitCond(&state->eventThreadCond);

    switch (pParams->transport) {
    case kJdwpTransportSocket:
        // LOGD("prepping for JDWP over TCP\n");
        state->transport = dvmJdwpSocketTransport();
        break;
    case kJdwpTransportAndroidAdb:
        // LOGD("prepping for JDWP over ADB\n");
        state->transport = dvmJdwpAndroidAdbTransport();
        /* TODO */
        break;
    default:
        LOGE("Unknown transport %d\n", pParams->transport);
        assert(false);
        goto fail;
    }

    if (!dvmJdwpNetStartup(state, pParams))
        goto fail;

    /*
     * Grab a mutex or two before starting the thread.  This ensures they
     * won't signal the cond var before we're waiting.
     */
    dvmDbgLockMutex(&state->threadStartLock);
    if (pParams->suspend)
        dvmDbgLockMutex(&state->attachLock);

    /*
     * We have bound to a port, or are trying to connect outbound to a
     * debugger.  Create the JDWP thread and let it continue the mission.
     */
    if (!dvmCreateInternalThread(&state->debugThreadHandle, "JDWP",
            jdwpThreadStart, state))
    {
        /* state is getting tossed, but unlock these anyway for cleanliness */
        dvmDbgUnlockMutex(&state->threadStartLock);
        if (pParams->suspend)
            dvmDbgUnlockMutex(&state->attachLock);
        goto fail;
    }

    /*
     * Wait until the thread finishes basic initialization.
     * TODO: cond vars should be waited upon in a loop
     */
    dvmDbgCondWait(&state->threadStartCond, &state->threadStartLock);
    dvmDbgUnlockMutex(&state->threadStartLock);


    /*
     * For suspend=y, wait for the debugger to connect to us or for us to
     * connect to the debugger.
     *
     * The JDWP thread will signal us when it connects successfully or
     * times out (for timeout=xxx), so we have to check to see what happened
     * when we wake up.
     */
    if (pParams->suspend) {
        dvmChangeStatus(NULL, THREAD_VMWAIT);
        dvmDbgCondWait(&state->attachCond, &state->attachLock);
        dvmDbgUnlockMutex(&state->attachLock);
        dvmChangeStatus(NULL, THREAD_RUNNING);

        if (!dvmJdwpIsActive(state)) {
            LOGE("JDWP connection failed\n");
            goto fail;
        }

        LOGI("JDWP connected\n");

        /*
         * Ordinarily we would pause briefly to allow the debugger to set
         * breakpoints and so on, but for "suspend=y" the VM init code will
         * pause the VM when it sends the VM_START message.
         */
    }

    return state;

fail:
    dvmJdwpShutdown(state);     // frees state
    return NULL;
}
コード例 #26
0
ファイル: Sync.cpp プロジェクト: 1mobilesuper/platform_dalvik
/*
 * Implements monitorenter for "synchronized" stuff.
 *
 * This does not fail or throw an exception (unless deadlock prediction
 * is enabled and set to "err" mode).
 */
void dvmLockObject(Thread* self, Object *obj)
{
    volatile u4 *thinp;
    ThreadStatus oldStatus;
    struct timespec tm;
    long sleepDelayNs;
    long minSleepDelayNs = 1000000;  /* 1 millisecond */
    long maxSleepDelayNs = 1000000000;  /* 1 second */
    u4 thin, newThin, threadId;

    assert(self != NULL);
    assert(obj != NULL);
    threadId = self->threadId;
    thinp = &obj->lock;
retry:
    thin = *thinp;
    if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
        /*
         * The lock is a thin lock.  The owner field is used to
         * determine the acquire method, ordered by cost.
         */
        if (LW_LOCK_OWNER(thin) == threadId) {
            /*
             * The calling thread owns the lock.  Increment the
             * value of the recursion count field.
             */
            obj->lock += 1 << LW_LOCK_COUNT_SHIFT;
            if (LW_LOCK_COUNT(obj->lock) == LW_LOCK_COUNT_MASK) {
                /*
                 * The reacquisition limit has been reached.  Inflate
                 * the lock so the next acquire will not overflow the
                 * recursion count field.
                 */
                inflateMonitor(self, obj);
            }
        } else if (LW_LOCK_OWNER(thin) == 0) {
            /*
             * The lock is unowned.  Install the thread id of the
             * calling thread into the owner field.  This is the
             * common case.  In performance critical code the JIT
             * will have tried this before calling out to the VM.
             */
            newThin = thin | (threadId << LW_LOCK_OWNER_SHIFT);
            if (android_atomic_acquire_cas(thin, newThin,
                    (int32_t*)thinp) != 0) {
                /*
                 * The acquire failed.  Try again.
                 */
                goto retry;
            }
        } else {
            ALOGV("(%d) spin on lock %p: %#x (%#x) %#x",
                 threadId, &obj->lock, 0, *thinp, thin);
            /*
             * The lock is owned by another thread.  Notify the VM
             * that we are about to wait.
             */
            oldStatus = dvmChangeStatus(self, THREAD_MONITOR);
            /*
             * Spin until the thin lock is released or inflated.
             */
            sleepDelayNs = 0;
            for (;;) {
                thin = *thinp;
                /*
                 * Check the shape of the lock word.  Another thread
                 * may have inflated the lock while we were waiting.
                 */
                if (LW_SHAPE(thin) == LW_SHAPE_THIN) {
                    if (LW_LOCK_OWNER(thin) == 0) {
                        /*
                         * The lock has been released.  Install the
                         * thread id of the calling thread into the
                         * owner field.
                         */
                        newThin = thin | (threadId << LW_LOCK_OWNER_SHIFT);
                        if (android_atomic_acquire_cas(thin, newThin,
                                (int32_t *)thinp) == 0) {
                            /*
                             * The acquire succeed.  Break out of the
                             * loop and proceed to inflate the lock.
                             */
                            break;
                        }
                    } else {
                        /*
                         * The lock has not been released.  Yield so
                         * the owning thread can run.
                         */
                        if (sleepDelayNs == 0) {
                            sched_yield();
                            sleepDelayNs = minSleepDelayNs;
                        } else {
                            tm.tv_sec = 0;
                            tm.tv_nsec = sleepDelayNs;
                            nanosleep(&tm, NULL);
                            /*
                             * Prepare the next delay value.  Wrap to
                             * avoid once a second polls for eternity.
                             */
                            if (sleepDelayNs < maxSleepDelayNs / 2) {
                                sleepDelayNs *= 2;
                            } else {
                                sleepDelayNs = minSleepDelayNs;
                            }
                        }
                    }
                } else {
                    /*
                     * The thin lock was inflated by another thread.
                     * Let the VM know we are no longer waiting and
                     * try again.
                     */
                    ALOGV("(%d) lock %p surprise-fattened",
                             threadId, &obj->lock);
                    dvmChangeStatus(self, oldStatus);
                    goto retry;
                }
            }
            ALOGV("(%d) spin on lock done %p: %#x (%#x) %#x",
                 threadId, &obj->lock, 0, *thinp, thin);
            /*
             * We have acquired the thin lock.  Let the VM know that
             * we are no longer waiting.
             */
            dvmChangeStatus(self, oldStatus);
            /*
             * Fatten the lock.
             */
            inflateMonitor(self, obj);
            ALOGV("(%d) lock %p fattened", threadId, &obj->lock);
        }
    } else {
        /*
         * The lock is a fat lock.
         */
        assert(LW_MONITOR(obj->lock) != NULL);
        lockMonitor(self, LW_MONITOR(obj->lock));
    }
}
コード例 #27
0
ファイル: Sync.cpp プロジェクト: 1mobilesuper/platform_dalvik
/*
 * Lock a monitor.
 */
static void lockMonitor(Thread* self, Monitor* mon)
{
    ThreadStatus oldStatus;
    u4 waitThreshold, samplePercent;
    u8 waitStart, waitEnd, waitMs;

    if (mon->owner == self) {
        mon->lockCount++;
        return;
    }
    if (dvmTryLockMutex(&mon->lock) != 0) {
        oldStatus = dvmChangeStatus(self, THREAD_MONITOR);
        waitThreshold = gDvm.lockProfThreshold;
        if (waitThreshold) {
            waitStart = dvmGetRelativeTimeUsec();
        }

        const Method* currentOwnerMethod = mon->ownerMethod;
        u4 currentOwnerPc = mon->ownerPc;

        dvmLockMutex(&mon->lock);
        if (waitThreshold) {
            waitEnd = dvmGetRelativeTimeUsec();
        }
        dvmChangeStatus(self, oldStatus);
        if (waitThreshold) {
            waitMs = (waitEnd - waitStart) / 1000;
            if (waitMs >= waitThreshold) {
                samplePercent = 100;
            } else {
                samplePercent = 100 * waitMs / waitThreshold;
            }
            if (samplePercent != 0 && ((u4)rand() % 100 < samplePercent)) {
                const char* currentOwnerFileName = "no_method";
                u4 currentOwnerLineNumber = 0;
                if (currentOwnerMethod != NULL) {
                    currentOwnerFileName = dvmGetMethodSourceFile(currentOwnerMethod);
                    if (currentOwnerFileName == NULL) {
                        currentOwnerFileName = "no_method_file";
                    }
                    currentOwnerLineNumber = dvmLineNumFromPC(currentOwnerMethod, currentOwnerPc);
                }
                logContentionEvent(self, waitMs, samplePercent,
                                   currentOwnerFileName, currentOwnerLineNumber);
            }
        }
    }
    mon->owner = self;
    assert(mon->lockCount == 0);

    // When debugging, save the current monitor holder for future
    // acquisition failures to use in sampled logging.
    if (gDvm.lockProfThreshold > 0) {
        mon->ownerMethod = NULL;
        mon->ownerPc = 0;
        if (self->interpSave.curFrame == NULL) {
            return;
        }
        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame);
        if (saveArea == NULL) {
            return;
        }
        mon->ownerMethod = saveArea->method;
        mon->ownerPc = (saveArea->xtra.currentPc - saveArea->method->insns);
    }
}