Merge "Add -Wno-unused-but-set-variable to the Dalvik project."
diff --git a/vm/Init.cpp b/vm/Init.cpp
index 6efeb26..21214d6 100644
--- a/vm/Init.cpp
+++ b/vm/Init.cpp
@@ -1329,7 +1329,7 @@
 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
     sigaddset(&mask, SIGUSR2);      // used to investigate JIT internals
 #endif
-    //sigaddset(&mask, SIGPIPE);
+    sigaddset(&mask, SIGPIPE);
     cc = sigprocmask(SIG_BLOCK, &mask, NULL);
     assert(cc == 0);
 
diff --git a/vm/Profile.cpp b/vm/Profile.cpp
index dfb50b3..08e1463 100644
--- a/vm/Profile.cpp
+++ b/vm/Profile.cpp
@@ -118,11 +118,12 @@
  * We use this clock when we can because it enables us to track the time that
  * a thread spends running and not blocked.
  */
-static inline u8 getThreadCpuTimeInUsec()
+static inline u8 getThreadCpuTimeInUsec(Thread* thread)
 {
+    clockid_t cid;
     struct timespec tm;
-
-    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);
+    pthread_getcpuclockid(thread->handle, &cid);
+    clock_gettime(cid, &tm);
     if (!(tm.tv_nsec >= 0 && tm.tv_nsec < 1*1000*1000*1000)) {
         ALOGE("bad nsec: %ld", tm.tv_nsec);
         dvmAbort();
@@ -137,7 +138,7 @@
 static inline u8 getStopwatchClock()
 {
 #if defined(HAVE_POSIX_CLOCKS)
-    return getThreadCpuTimeInUsec();
+    return getThreadCpuTimeInUsec(dvmThreadSelf());
 #else
     return getWallTimeInUsec();
 #endif
@@ -171,6 +172,133 @@
 }
 
 /*
+ * Gets a thread's stack trace as an array of method pointers of length pCount.
+ * The returned array must be freed by the caller.
+ */
+static const Method** getStackTrace(Thread* thread, size_t* pCount)
+{
+    void* fp = thread->interpSave.curFrame;
+    assert(thread == dvmThreadSelf() || dvmIsSuspended(thread));
+
+    /* Compute the stack depth. */
+    size_t stackDepth = 0;
+    while (fp != NULL) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+
+        if (!dvmIsBreakFrame((u4*) fp))
+            stackDepth++;
+
+        assert(fp != saveArea->prevFrame);
+        fp = saveArea->prevFrame;
+    }
+    *pCount = stackDepth;
+
+    /*
+     * Allocate memory for stack trace. This must be freed later, either by
+     * freeThreadStackTraceSamples when tracing stops or by freeThread.
+     */
+    const Method** stackTrace = (const Method**) malloc(sizeof(Method*) *
+                                                        stackDepth);
+    if (stackTrace == NULL)
+        return NULL;
+
+    /* Walk the stack a second time, filling in the stack trace. */
+    const Method** ptr = stackTrace;
+    fp = thread->interpSave.curFrame;
+    while (fp != NULL) {
+        const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+        const Method* method = saveArea->method;
+
+        if (!dvmIsBreakFrame((u4*) fp)) {
+            *ptr++ = method;
+            stackDepth--;
+        }
+        assert(fp != saveArea->prevFrame);
+        fp = saveArea->prevFrame;
+    }
+    assert(stackDepth == 0);
+
+    return stackTrace;
+}
+/*
+ * Get a sample of the stack trace for a thread.
+ */
+static void getSample(Thread* thread)
+{
+    /* Get old and new stack trace for thread. */
+    size_t newLength = 0;
+    const Method** newStackTrace = getStackTrace(thread, &newLength);
+    size_t oldLength = thread->stackTraceSampleLength;
+    const Method** oldStackTrace = thread->stackTraceSample;
+
+    /* Read time clocks to use for all events in this trace. */
+    u4 cpuClockDiff = 0;
+    u4 wallClockDiff = 0;
+    dvmMethodTraceReadClocks(thread, &cpuClockDiff, &wallClockDiff);
+    if (oldStackTrace == NULL) {
+        /*
+         * If there's no previous stack trace sample, log an entry event for
+         * every method in the trace.
+         */
+        for (int i = newLength - 1; i >= 0; --i) {
+            dvmMethodTraceAdd(thread, newStackTrace[i], METHOD_TRACE_ENTER,
+                              cpuClockDiff, wallClockDiff);
+        }
+    } else {
+        /*
+         * If there's a previous stack trace, diff the traces and emit entry
+         * and exit events accordingly.
+         */
+        int diffIndexOld = oldLength - 1;
+        int diffIndexNew = newLength - 1;
+        /* Iterate bottom-up until there's a difference between traces. */
+        while (diffIndexOld >= 0 && diffIndexNew >= 0 &&
+               oldStackTrace[diffIndexOld] == newStackTrace[diffIndexNew]) {
+            diffIndexOld--;
+            diffIndexNew--;
+        }
+        /* Iterate top-down over old trace until diff, emitting exit events. */
+        for (int i = 0; i <= diffIndexOld; ++i) {
+            dvmMethodTraceAdd(thread, oldStackTrace[i], METHOD_TRACE_EXIT,
+                              cpuClockDiff, wallClockDiff);
+        }
+        /* Iterate bottom-up over new trace from diff, emitting entry events. */
+        for (int i = diffIndexNew; i >= 0; --i) {
+            dvmMethodTraceAdd(thread, newStackTrace[i], METHOD_TRACE_ENTER,
+                              cpuClockDiff, wallClockDiff);
+        }
+    }
+
+    /* Free the old stack trace and update the thread's stack trace sample. */
+    free(oldStackTrace);
+    thread->stackTraceSample = newStackTrace;
+    thread->stackTraceSampleLength = newLength;
+}
+
+/*
+ * Entry point for sampling thread. The sampling interval in microseconds is
+ * passed in as an argument.
+ */
+static void* runSamplingThread(void* arg)
+{
+    int intervalUs = (int) arg;
+    while (gDvm.methodTrace.traceEnabled) {
+        dvmSuspendAllThreads(SUSPEND_FOR_SAMPLING);
+
+        dvmLockThreadList(dvmThreadSelf());
+        for (Thread *thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+            getSample(thread);
+        }
+        dvmUnlockThreadList();
+
+        dvmResumeAllThreads(SUSPEND_FOR_SAMPLING);
+
+        usleep(intervalUs);
+    }
+    return NULL;
+}
+
+/*
  * Boot-time init.
  */
 bool dvmProfilingStartup()
@@ -282,6 +410,21 @@
 }
 
 /*
+ * Free and reset the "stackTraceSample" field in all threads.
+ */
+static void freeThreadStackTraceSamples()
+{
+    Thread* thread;
+
+    dvmLockThreadList(NULL);
+    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
+        free(thread->stackTraceSample);
+        thread->stackTraceSample = NULL;
+    }
+    dvmUnlockThreadList();
+}
+
+/*
  * Dump the thread list to the specified file.
  */
 static void dumpThreadList(FILE* fp) {
@@ -359,7 +502,7 @@
  * On failure, we throw an exception and return.
  */
 void dvmMethodTraceStart(const char* traceFileName, int traceFd, int bufferSize,
-    int flags, bool directToDdms)
+    int flags, bool directToDdms, bool samplingEnabled, int intervalUs)
 {
     MethodTraceState* state = &gDvm.methodTrace;
 
@@ -428,6 +571,8 @@
         state->recordSize = TRACE_REC_SIZE_SINGLE_CLOCK;
     }
 
+    state->samplingEnabled = samplingEnabled;
+
     /*
      * Output the header.
      */
@@ -452,7 +597,17 @@
      * following to take a Thread* argument, and set the appropriate
      * interpBreak flags only on the target thread.
      */
-    updateActiveProfilers(kSubModeMethodTrace, true);
+    if (samplingEnabled) {
+        updateActiveProfilers(kSubModeSampleTrace, true);
+        /* Start the sampling thread. */
+        if (!dvmCreateInternalThread(&state->samplingThreadHandle,
+                "Sampling Thread", &runSamplingThread, (void*) intervalUs)) {
+            dvmThrowInternalError("failed to create sampling thread");
+            goto fail;
+        }
+    } else {
+        updateActiveProfilers(kSubModeMethodTrace, true);
+    }
 
     dvmUnlockMutex(&state->startStopLock);
     return;
@@ -500,7 +655,7 @@
 {
 #if defined(HAVE_POSIX_CLOCKS)
     if (useThreadCpuClock()) {
-        getThreadCpuTimeInUsec();
+        getThreadCpuTimeInUsec(dvmThreadSelf());
     }
 #endif
     if (useWallClock()) {
@@ -551,6 +706,7 @@
 void dvmMethodTraceStop()
 {
     MethodTraceState* state = &gDvm.methodTrace;
+    bool samplingEnabled = state->samplingEnabled;
     u8 elapsed;
 
     /*
@@ -565,7 +721,11 @@
         dvmUnlockMutex(&state->startStopLock);
         return;
     } else {
-        updateActiveProfilers(kSubModeMethodTrace, false);
+        if (samplingEnabled) {
+            updateActiveProfilers(kSubModeSampleTrace, false);
+        } else {
+            updateActiveProfilers(kSubModeMethodTrace, false);
+        }
     }
 
     /* compute elapsed time */
@@ -719,9 +879,42 @@
     fclose(state->traceFile);
     state->traceFile = NULL;
 
+    /* free and clear sampling traces held by all threads */
+    if (samplingEnabled) {
+        freeThreadStackTraceSamples();
+    }
+
     /* wake any threads that were waiting for profiling to complete */
     dvmBroadcastCond(&state->threadExitCond);
     dvmUnlockMutex(&state->startStopLock);
+
+    /* make sure the sampling thread has stopped */
+    if (samplingEnabled &&
+        pthread_join(state->samplingThreadHandle, NULL) != 0) {
+        ALOGW("Sampling thread join failed");
+    }
+}
+
+/*
+ * Read clocks and generate time diffs for method trace events.
+ */
+void dvmMethodTraceReadClocks(Thread* self, u4* cpuClockDiff,
+                              u4* wallClockDiff)
+{
+#if defined(HAVE_POSIX_CLOCKS)
+    if (useThreadCpuClock()) {
+        if (!self->cpuClockBaseSet) {
+            /* Initialize per-thread CPU clock base time on first use. */
+            self->cpuClockBase = getThreadCpuTimeInUsec(self);
+            self->cpuClockBaseSet = true;
+        } else {
+            *cpuClockDiff = getThreadCpuTimeInUsec(self) - self->cpuClockBase;
+        }
+    }
+#endif
+    if (useWallClock()) {
+        *wallClockDiff = getWallTimeInUsec() - gDvm.methodTrace.startWhen;
+    }
 }
 
 /*
@@ -730,7 +923,8 @@
  * Multiple threads may be banging on this all at once.  We use atomic ops
  * rather than mutexes for speed.
  */
-void dvmMethodTraceAdd(Thread* self, const Method* method, int action)
+void dvmMethodTraceAdd(Thread* self, const Method* method, int action,
+                       u4 cpuClockDiff, u4 wallClockDiff)
 {
     MethodTraceState* state = &gDvm.methodTrace;
     u4 methodVal;
@@ -739,21 +933,6 @@
 
     assert(method != NULL);
 
-#if defined(HAVE_POSIX_CLOCKS)
-    /*
-     * We can only access the per-thread CPU clock from within the
-     * thread, so we have to initialize the base time on the first use.
-     * (Looks like pthread_getcpuclockid(thread, &id) will do what we
-     * want, but it doesn't appear to be defined on the device.)
-     */
-    if (!self->cpuClockBaseSet) {
-        self->cpuClockBase = getThreadCpuTimeInUsec();
-        self->cpuClockBaseSet = true;
-        //ALOGI("thread base id=%d 0x%llx",
-        //    self->threadId, self->cpuClockBase);
-    }
-#endif
-
     /*
      * Advance "curOffset" atomically.
      */
@@ -784,7 +963,6 @@
 
 #if defined(HAVE_POSIX_CLOCKS)
     if (useThreadCpuClock()) {
-        u4 cpuClockDiff = (u4) (getThreadCpuTimeInUsec() - self->cpuClockBase);
         *ptr++ = (u1) cpuClockDiff;
         *ptr++ = (u1) (cpuClockDiff >> 8);
         *ptr++ = (u1) (cpuClockDiff >> 16);
@@ -793,7 +971,6 @@
 #endif
 
     if (useWallClock()) {
-        u4 wallClockDiff = (u4) (getWallTimeInUsec() - state->startWhen);
         *ptr++ = (u1) wallClockDiff;
         *ptr++ = (u1) (wallClockDiff >> 8);
         *ptr++ = (u1) (wallClockDiff >> 16);
@@ -809,7 +986,11 @@
 void dvmFastMethodTraceEnter(const Method* method, Thread* self)
 {
     if (self->interpBreak.ctl.subMode & kSubModeMethodTrace) {
-        dvmMethodTraceAdd(self, method, METHOD_TRACE_ENTER);
+        u4 cpuClockDiff = 0;
+        u4 wallClockDiff = 0;
+        dvmMethodTraceReadClocks(self, &cpuClockDiff, &wallClockDiff);
+        dvmMethodTraceAdd(self, method, METHOD_TRACE_ENTER, cpuClockDiff,
+                          wallClockDiff);
     }
 }
 
@@ -821,8 +1002,11 @@
 void dvmFastMethodTraceExit(Thread* self)
 {
     if (self->interpBreak.ctl.subMode & kSubModeMethodTrace) {
+        u4 cpuClockDiff = 0;
+        u4 wallClockDiff = 0;
+        dvmMethodTraceReadClocks(self, &cpuClockDiff, &wallClockDiff);
         dvmMethodTraceAdd(self, self->interpSave.method,
-                          METHOD_TRACE_EXIT);
+                          METHOD_TRACE_EXIT, cpuClockDiff, wallClockDiff);
     }
 }
 
@@ -834,7 +1018,11 @@
 void dvmFastNativeMethodTraceExit(const Method* method, Thread* self)
 {
     if (self->interpBreak.ctl.subMode & kSubModeMethodTrace) {
-        dvmMethodTraceAdd(self, method, METHOD_TRACE_EXIT);
+        u4 cpuClockDiff = 0;
+        u4 wallClockDiff = 0;
+        dvmMethodTraceReadClocks(self, &cpuClockDiff, &wallClockDiff);
+        dvmMethodTraceAdd(self, method, METHOD_TRACE_EXIT, cpuClockDiff,
+                          wallClockDiff);
     }
 }
 
diff --git a/vm/Profile.h b/vm/Profile.h
index 6c9e1c7..6a2c4be 100644
--- a/vm/Profile.h
+++ b/vm/Profile.h
@@ -52,6 +52,9 @@
 
     int     traceVersion;
     size_t  recordSize;
+
+    bool    samplingEnabled;
+    pthread_t       samplingThreadHandle;
 };
 
 /*
@@ -83,7 +86,7 @@
  * Start/stop method tracing.
  */
 void dvmMethodTraceStart(const char* traceFileName, int traceFd, int bufferSize,
-        int flags, bool directToDdms);
+        int flags, bool directToDdms, bool samplingEnabled, int intervalUs);
 bool dvmIsMethodTraceActive(void);
 void dvmMethodTraceStop(void);
 
@@ -112,27 +115,45 @@
  */
 #define TRACE_METHOD_ENTER(_self, _method)                                  \
     do {                                                                    \
-        if (_self->interpBreak.ctl.subMode & kSubModeMethodTrace)           \
-            dvmMethodTraceAdd(_self, _method, METHOD_TRACE_ENTER);          \
+        if (_self->interpBreak.ctl.subMode & kSubModeMethodTrace) {         \
+            u4 cpuClockDiff = 0;                                            \
+            u4 wallClockDiff = 0;                                           \
+            dvmMethodTraceReadClocks(_self, &cpuClockDiff, &wallClockDiff); \
+            dvmMethodTraceAdd(_self, _method, METHOD_TRACE_ENTER,           \
+                              cpuClockDiff, wallClockDiff);                 \
+        }                                                                   \
         if (_self->interpBreak.ctl.subMode & kSubModeEmulatorTrace)         \
             dvmEmitEmulatorTrace(_method, METHOD_TRACE_ENTER);              \
     } while(0);
 #define TRACE_METHOD_EXIT(_self, _method)                                   \
     do {                                                                    \
-        if (_self->interpBreak.ctl.subMode & kSubModeMethodTrace)           \
-            dvmMethodTraceAdd(_self, _method, METHOD_TRACE_EXIT);           \
+        if (_self->interpBreak.ctl.subMode & kSubModeMethodTrace) {         \
+            u4 cpuClockDiff = 0;                                            \
+            u4 wallClockDiff = 0;                                           \
+            dvmMethodTraceReadClocks(_self, &cpuClockDiff, &wallClockDiff); \
+            dvmMethodTraceAdd(_self, _method, METHOD_TRACE_EXIT,            \
+                              cpuClockDiff, wallClockDiff);                 \
+        }                                                                   \
         if (_self->interpBreak.ctl.subMode & kSubModeEmulatorTrace)         \
             dvmEmitEmulatorTrace(_method, METHOD_TRACE_EXIT);               \
     } while(0);
 #define TRACE_METHOD_UNROLL(_self, _method)                                 \
     do {                                                                    \
-        if (_self->interpBreak.ctl.subMode & kSubModeMethodTrace)           \
-            dvmMethodTraceAdd(_self, _method, METHOD_TRACE_UNROLL);         \
+        if (_self->interpBreak.ctl.subMode & kSubModeMethodTrace) {         \
+            u4 cpuClockDiff = 0;                                            \
+            u4 wallClockDiff = 0;                                           \
+            dvmMethodTraceReadClocks(_self, &cpuClockDiff, &wallClockDiff); \
+            dvmMethodTraceAdd(_self, _method, METHOD_TRACE_UNROLL,          \
+                              cpuClockDiff, wallClockDiff);                 \
+        }                                                                   \
         if (_self->interpBreak.ctl.subMode & kSubModeEmulatorTrace)         \
             dvmEmitEmulatorTrace(_method, METHOD_TRACE_UNROLL);             \
     } while(0);
 
-void dvmMethodTraceAdd(struct Thread* self, const Method* method, int action);
+void dvmMethodTraceReadClocks(Thread* self, u4* cpuClockDiff,
+                              u4* wallClockDiff);
+void dvmMethodTraceAdd(struct Thread* self, const Method* method, int action,
+                       u4 cpuClockDiff, u4 wallClockDiff);
 void dvmEmitEmulatorTrace(const Method* method, int action);
 
 void dvmMethodTraceGCBegin(void);
diff --git a/vm/Thread.cpp b/vm/Thread.cpp
index 205167e..1ebfca7 100644
--- a/vm/Thread.cpp
+++ b/vm/Thread.cpp
@@ -997,6 +997,7 @@
 #if defined(WITH_SELF_VERIFICATION)
     dvmSelfVerificationShadowSpaceFree(thread);
 #endif
+    free(thread->stackTraceSample);
     free(thread);
 }
 
diff --git a/vm/Thread.h b/vm/Thread.h
index 8deef6e..19bd49c 100644
--- a/vm/Thread.h
+++ b/vm/Thread.h
@@ -287,6 +287,10 @@
     bool        cpuClockBaseSet;
     u8          cpuClockBase;
 
+    /* previous stack trace sample and length (used by sampling profiler) */
+    const Method** stackTraceSample;
+    size_t stackTraceSampleLength;
+
     /* memory allocation profiling state */
     AllocProfState allocProf;
 
@@ -358,6 +362,7 @@
     SUSPEND_FOR_DEX_OPT,
     SUSPEND_FOR_VERIFY,
     SUSPEND_FOR_HPROF,
+    SUSPEND_FOR_SAMPLING,
 #if defined(WITH_JIT)
     SUSPEND_FOR_TBL_RESIZE,  // jit-table resize
     SUSPEND_FOR_IC_PATCH,    // polymorphic callsite inline-cache patch
diff --git a/vm/interp/Interp.cpp b/vm/interp/Interp.cpp
index fa77523..2b13bd8 100644
--- a/vm/interp/Interp.cpp
+++ b/vm/interp/Interp.cpp
@@ -1661,7 +1661,11 @@
         dvmEnableSubMode(thread, kSubModeInstCounting);
     }
     if (dvmIsMethodTraceActive()) {
-        dvmEnableSubMode(thread, kSubModeMethodTrace);
+        if (gDvm.methodTrace.samplingEnabled) {
+            dvmEnableSubMode(thread, kSubModeSampleTrace);
+        } else {
+            dvmEnableSubMode(thread, kSubModeMethodTrace);
+        }
     }
     if (gDvm.emulatorTraceEnableCount > 0) {
         dvmEnableSubMode(thread, kSubModeEmulatorTrace);
diff --git a/vm/interp/InterpState.h b/vm/interp/InterpState.h
index cc0a13f..6ef4472 100644
--- a/vm/interp/InterpState.h
+++ b/vm/interp/InterpState.h
@@ -63,6 +63,7 @@
     kSubModeCallbackPending   = 0x0020,
     kSubModeCountedStep       = 0x0040,
     kSubModeCheckAlways       = 0x0080,
+    kSubModeSampleTrace       = 0x0100,
     kSubModeJitTraceBuild     = 0x4000,
     kSubModeJitSV             = 0x8000,
     kSubModeDebugProfile   = (kSubModeMethodTrace |
diff --git a/vm/native/dalvik_system_VMDebug.cpp b/vm/native/dalvik_system_VMDebug.cpp
index f6d91a2..3ebf7c3 100644
--- a/vm/native/dalvik_system_VMDebug.cpp
+++ b/vm/native/dalvik_system_VMDebug.cpp
@@ -55,6 +55,7 @@
     std::vector<std::string> features;
     features.push_back("method-trace-profiling");
     features.push_back("method-trace-profiling-streaming");
+    features.push_back("method-sample-profiling");
     features.push_back("hprof-heap-dump");
     features.push_back("hprof-heap-dump-streaming");
 
@@ -223,16 +224,30 @@
 }
 
 /*
- * static void startMethodTracingNative(String traceFileName,
- *     FileDescriptor fd, int bufferSize, int flags)
+ * static void startMethodTracingDdmsImpl(int bufferSize, int flags,
+ *     boolean samplingEnabled, int intervalUs)
  *
- * Start method trace profiling.
- *
- * If both "traceFileName" and "fd" are null, the result will be sent
- * directly to DDMS.  (The non-DDMS versions of the calls are expected
- * to enforce non-NULL filenames.)
+ * Start method trace profiling, sending results directly to DDMS.
  */
-static void Dalvik_dalvik_system_VMDebug_startMethodTracingNative(const u4* args,
+static void Dalvik_dalvik_system_VMDebug_startMethodTracingDdmsImpl(const u4* args,
+    JValue* pResult)
+{
+    int bufferSize = args[0];
+    int flags = args[1];
+    bool samplingEnabled = args[2];
+    int intervalUs = args[3];
+    dvmMethodTraceStart("[DDMS]", -1, bufferSize, flags, true, samplingEnabled,
+        intervalUs);
+    RETURN_VOID();
+}
+
+/*
+ * static void startMethodTracingFd(String traceFileName, FileDescriptor fd,
+ *     int bufferSize, int flags)
+ *
+ * Start method trace profiling, sending results to a file descriptor.
+ */
+static void Dalvik_dalvik_system_VMDebug_startMethodTracingFd(const u4* args,
     JValue* pResult)
 {
     StringObject* traceFileStr = (StringObject*) args[0];
@@ -240,36 +255,46 @@
     int bufferSize = args[2];
     int flags = args[3];
 
-    if (bufferSize == 0) {
-        // Default to 8MB per the documentation.
-        bufferSize = 8 * 1024 * 1024;
-    }
+    int origFd = getFileDescriptor(traceFd);
+    if (origFd < 0)
+        RETURN_VOID();
 
-    if (bufferSize < 1024) {
-        dvmThrowIllegalArgumentException(NULL);
+    int fd = dup(origFd);
+    if (fd < 0) {
+        dvmThrowExceptionFmt(gDvm.exRuntimeException,
+            "dup(%d) failed: %s", origFd, strerror(errno));
         RETURN_VOID();
     }
 
-    char* traceFileName = NULL;
-    if (traceFileStr != NULL)
-        traceFileName = dvmCreateCstrFromString(traceFileStr);
-
-    int fd = -1;
-    if (traceFd != NULL) {
-        int origFd = getFileDescriptor(traceFd);
-        if (origFd < 0)
-            RETURN_VOID();
-
-        fd = dup(origFd);
-        if (fd < 0) {
-            dvmThrowExceptionFmt(gDvm.exRuntimeException,
-                "dup(%d) failed: %s", origFd, strerror(errno));
-            RETURN_VOID();
-        }
+    char* traceFileName = dvmCreateCstrFromString(traceFileStr);
+    if (traceFileName == NULL) {
+        RETURN_VOID();
     }
 
-    dvmMethodTraceStart(traceFileName != NULL ? traceFileName : "[DDMS]",
-        fd, bufferSize, flags, (traceFileName == NULL && fd == -1));
+    dvmMethodTraceStart(traceFileName, fd, bufferSize, flags, false, false, 0);
+    free(traceFileName);
+    RETURN_VOID();
+}
+
+/*
+ * static void startMethodTracingFilename(String traceFileName, int bufferSize,
+ *     int flags)
+ *
+ * Start method trace profiling, sending results to a file.
+ */
+static void Dalvik_dalvik_system_VMDebug_startMethodTracingFilename(const u4* args,
+    JValue* pResult)
+{
+    StringObject* traceFileStr = (StringObject*) args[0];
+    int bufferSize = args[1];
+    int flags = args[2];
+
+    char* traceFileName = dvmCreateCstrFromString(traceFileStr);
+    if (traceFileName == NULL) {
+        RETURN_VOID();
+    }
+
+    dvmMethodTraceStart(traceFileName, -1, bufferSize, flags, false, false, 0);
     free(traceFileName);
     RETURN_VOID();
 }
@@ -756,8 +781,12 @@
         Dalvik_dalvik_system_VMDebug_startAllocCounting },
     { "stopAllocCounting",          "()V",
         Dalvik_dalvik_system_VMDebug_stopAllocCounting },
-    { "startMethodTracingNative",   "(Ljava/lang/String;Ljava/io/FileDescriptor;II)V",
-        Dalvik_dalvik_system_VMDebug_startMethodTracingNative },
+    { "startMethodTracingDdmsImpl", "(IIZI)V",
+        Dalvik_dalvik_system_VMDebug_startMethodTracingDdmsImpl },
+    { "startMethodTracingFd",       "(Ljava/lang/String;Ljava/io/FileDescriptor;II)V",
+        Dalvik_dalvik_system_VMDebug_startMethodTracingFd },
+    { "startMethodTracingFilename", "(Ljava/lang/String;II)V",
+        Dalvik_dalvik_system_VMDebug_startMethodTracingFilename },
     { "isMethodTracingActive",      "()Z",
         Dalvik_dalvik_system_VMDebug_isMethodTracingActive },
     { "stopMethodTracing",          "()V",