1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * Android's method call profiling goodies.
19 */
20#include "Dalvik.h"
21
22#ifdef WITH_PROFILER        // -- include rest of file
23
24#include <stdlib.h>
25#include <stddef.h>
26#include <string.h>
27#include <sys/time.h>
28#include <time.h>
29#include <sys/mman.h>
30#include <sched.h>
31#include <errno.h>
32#include <fcntl.h>
33
34#include <cutils/open_memstream.h>
35
36#ifdef HAVE_ANDROID_OS
37# define UPDATE_MAGIC_PAGE      1
38#endif
39
40/*
41 * File format:
42 *  header
43 *  record 0
44 *  record 1
45 *  ...
46 *
47 * Header format:
48 *  u4  magic ('SLOW')
49 *  u2  version
50 *  u2  offset to data
51 *  u8  start date/time in usec
52 *
53 * Record format:
54 *  u1  thread ID
55 *  u4  method ID | method action
56 *  u4  time delta since start, in usec
57 *
58 * 32 bits of microseconds is 70 minutes.
59 *
60 * All values are stored in little-endian order.
61 */
62#define TRACE_REC_SIZE      9
63#define TRACE_MAGIC         0x574f4c53
64#define TRACE_HEADER_LEN    32
65
66#define FILL_PATTERN        0xeeeeeeee
67
68
69/*
70 * Get the wall-clock date/time, in usec.
71 */
72static inline u8 getTimeInUsec()
73{
74    struct timeval tv;
75
76    gettimeofday(&tv, NULL);
77    return tv.tv_sec * 1000000LL + tv.tv_usec;
78}
79
80/*
81 * Get the current time, in microseconds.
82 *
83 * This can mean one of two things.  In "global clock" mode, we get the
84 * same time across all threads.  If we use CLOCK_THREAD_CPUTIME_ID, we
85 * get a per-thread CPU usage timer.  The latter is better, but a bit
86 * more complicated to implement.
87 */
88static inline u8 getClock()
89{
90#if defined(HAVE_POSIX_CLOCKS)
91    struct timespec tm;
92
93    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);
94    //assert(tm.tv_nsec >= 0 && tm.tv_nsec < 1*1000*1000*1000);
95    if (!(tm.tv_nsec >= 0 && tm.tv_nsec < 1*1000*1000*1000)) {
96        LOGE("bad nsec: %ld\n", tm.tv_nsec);
97        dvmAbort();
98    }
99
100    return tm.tv_sec * 1000000LL + tm.tv_nsec / 1000;
101#else
102    struct timeval tv;
103
104    gettimeofday(&tv, NULL);
105    return tv.tv_sec * 1000000LL + tv.tv_usec;
106#endif
107}
108
109/*
110 * Write little-endian data.
111 */
112static inline void storeShortLE(u1* buf, u2 val)
113{
114    *buf++ = (u1) val;
115    *buf++ = (u1) (val >> 8);
116}
117static inline void storeIntLE(u1* buf, u4 val)
118{
119    *buf++ = (u1) val;
120    *buf++ = (u1) (val >> 8);
121    *buf++ = (u1) (val >> 16);
122    *buf++ = (u1) (val >> 24);
123}
124static inline void storeLongLE(u1* buf, u8 val)
125{
126    *buf++ = (u1) val;
127    *buf++ = (u1) (val >> 8);
128    *buf++ = (u1) (val >> 16);
129    *buf++ = (u1) (val >> 24);
130    *buf++ = (u1) (val >> 32);
131    *buf++ = (u1) (val >> 40);
132    *buf++ = (u1) (val >> 48);
133    *buf++ = (u1) (val >> 56);
134}
135
136/*
137 * Boot-time init.
138 */
139bool dvmProfilingStartup(void)
140{
141    /*
142     * Initialize "dmtrace" method profiling.
143     */
144    memset(&gDvm.methodTrace, 0, sizeof(gDvm.methodTrace));
145    dvmInitMutex(&gDvm.methodTrace.startStopLock);
146    pthread_cond_init(&gDvm.methodTrace.threadExitCond, NULL);
147
148    ClassObject* clazz =
149        dvmFindClassNoInit("Ldalvik/system/VMDebug;", NULL);
150    assert(clazz != NULL);
151    gDvm.methodTrace.gcMethod =
152        dvmFindDirectMethodByDescriptor(clazz, "startGC", "()V");
153    gDvm.methodTrace.classPrepMethod =
154        dvmFindDirectMethodByDescriptor(clazz, "startClassPrep", "()V");
155    if (gDvm.methodTrace.gcMethod == NULL ||
156        gDvm.methodTrace.classPrepMethod == NULL)
157    {
158        LOGE("Unable to find startGC or startClassPrep\n");
159        return false;
160    }
161
162    assert(!dvmCheckException(dvmThreadSelf()));
163
164    /*
165     * Allocate storage for instruction counters.
166     */
167    gDvm.executedInstrCounts = (int*) malloc(kNumDalvikInstructions * sizeof(int));
168    if (gDvm.executedInstrCounts == NULL)
169        return false;
170    memset(gDvm.executedInstrCounts, 0, kNumDalvikInstructions * sizeof(int));
171
172#ifdef UPDATE_MAGIC_PAGE
173    /*
174     * If we're running on the emulator, there's a magic page into which
175     * we can put interpreted method information.  This allows interpreted
176     * methods to show up in the emulator's code traces.
177     *
178     * We could key this off of the "ro.kernel.qemu" property, but there's
179     * no real harm in doing this on a real device.
180     */
181    int fd = open("/dev/qemu_trace", O_RDWR);
182    if (fd < 0) {
183        LOGV("Unable to open /dev/qemu_trace\n");
184    } else {
185        gDvm.emulatorTracePage = mmap(0, SYSTEM_PAGE_SIZE, PROT_READ|PROT_WRITE,
186                                      MAP_SHARED, fd, 0);
187        close(fd);
188        if (gDvm.emulatorTracePage == MAP_FAILED) {
189            LOGE("Unable to mmap /dev/qemu_trace\n");
190            gDvm.emulatorTracePage = NULL;
191        } else {
192            *(u4*) gDvm.emulatorTracePage = 0;
193        }
194    }
195#else
196    assert(gDvm.emulatorTracePage == NULL);
197#endif
198
199    return true;
200}
201
202/*
203 * Free up profiling resources.
204 */
205void dvmProfilingShutdown(void)
206{
207#ifdef UPDATE_MAGIC_PAGE
208    if (gDvm.emulatorTracePage != NULL)
209        munmap(gDvm.emulatorTracePage, SYSTEM_PAGE_SIZE);
210#endif
211    free(gDvm.executedInstrCounts);
212}
213
214/*
215 * Update the "active profilers" count.
216 *
217 * "count" should be +1 or -1.
218 */
219static void updateActiveProfilers(int count)
220{
221    int oldValue, newValue;
222
223    do {
224        oldValue = gDvm.activeProfilers;
225        newValue = oldValue + count;
226        if (newValue < 0) {
227            LOGE("Can't have %d active profilers\n", newValue);
228            dvmAbort();
229        }
230    } while (!ATOMIC_CMP_SWAP(&gDvm.activeProfilers, oldValue, newValue));
231
232    LOGD("+++ active profiler count now %d\n", newValue);
233#if defined(WITH_JIT)
234    dvmCompilerStateRefresh();
235#endif
236}
237
238
239/*
240 * Reset the "cpuClockBase" field in all threads.
241 */
242static void resetCpuClockBase(void)
243{
244    Thread* thread;
245
246    dvmLockThreadList(NULL);
247    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
248        thread->cpuClockBaseSet = false;
249        thread->cpuClockBase = 0;
250    }
251    dvmUnlockThreadList();
252}
253
254/*
255 * Dump the thread list to the specified file.
256 */
257static void dumpThreadList(FILE* fp)
258{
259    Thread* thread;
260
261    dvmLockThreadList(NULL);
262    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
263        char* name = dvmGetThreadName(thread);
264
265        fprintf(fp, "%d\t%s\n", thread->threadId, name);
266        free(name);
267    }
268    dvmUnlockThreadList();
269}
270
271/*
272 * This is a dvmHashForeach callback.
273 */
274static int dumpMarkedMethods(void* vclazz, void* vfp)
275{
276    DexStringCache stringCache;
277    ClassObject* clazz = (ClassObject*) vclazz;
278    FILE* fp = (FILE*) vfp;
279    Method* meth;
280    char* name;
281    int i, lineNum;
282
283    dexStringCacheInit(&stringCache);
284
285    for (i = 0; i < clazz->virtualMethodCount; i++) {
286        meth = &clazz->virtualMethods[i];
287        if (meth->inProfile) {
288            name = dvmDescriptorToName(meth->clazz->descriptor);
289            fprintf(fp, "0x%08x\t%s\t%s\t%s\t%s\t%d\n", (int) meth,
290                name, meth->name,
291                dexProtoGetMethodDescriptor(&meth->prototype, &stringCache),
292                dvmGetMethodSourceFile(meth), dvmLineNumFromPC(meth, 0));
293            meth->inProfile = false;
294            free(name);
295        }
296    }
297
298    for (i = 0; i < clazz->directMethodCount; i++) {
299        meth = &clazz->directMethods[i];
300        if (meth->inProfile) {
301            name = dvmDescriptorToName(meth->clazz->descriptor);
302            fprintf(fp, "0x%08x\t%s\t%s\t%s\t%s\t%d\n", (int) meth,
303                name, meth->name,
304                dexProtoGetMethodDescriptor(&meth->prototype, &stringCache),
305                dvmGetMethodSourceFile(meth), dvmLineNumFromPC(meth, 0));
306            meth->inProfile = false;
307            free(name);
308        }
309    }
310
311    dexStringCacheRelease(&stringCache);
312
313    return 0;
314}
315
316/*
317 * Dump the list of "marked" methods to the specified file.
318 */
319static void dumpMethodList(FILE* fp)
320{
321    dvmHashTableLock(gDvm.loadedClasses);
322    dvmHashForeach(gDvm.loadedClasses, dumpMarkedMethods, (void*) fp);
323    dvmHashTableUnlock(gDvm.loadedClasses);
324}
325
326/*
327 * Start method tracing.  Method tracing is global to the VM (i.e. we
328 * trace all threads).
329 *
330 * This opens the output file (if an already open fd has not been supplied,
331 * and we're not going direct to DDMS) and allocates the data buffer.
332 *
333 * On failure, we throw an exception and return.
334 */
335void dvmMethodTraceStart(const char* traceFileName, int traceFd, int bufferSize,
336    int flags, bool directToDdms)
337{
338    MethodTraceState* state = &gDvm.methodTrace;
339
340    assert(bufferSize > 0);
341
342    dvmLockMutex(&state->startStopLock);
343    while (state->traceEnabled != 0) {
344        LOGI("TRACE start requested, but already in progress; stopping\n");
345        dvmUnlockMutex(&state->startStopLock);
346        dvmMethodTraceStop();
347        dvmLockMutex(&state->startStopLock);
348    }
349    updateActiveProfilers(1);
350    LOGI("TRACE STARTED: '%s' %dKB\n", traceFileName, bufferSize / 1024);
351
352    /*
353     * Allocate storage and open files.
354     *
355     * We don't need to initialize the buffer, but doing so might remove
356     * some fault overhead if the pages aren't mapped until touched.
357     */
358    state->buf = (u1*) malloc(bufferSize);
359    if (state->buf == NULL) {
360        dvmThrowException("Ljava/lang/InternalError;", "buffer alloc failed");
361        goto fail;
362    }
363    if (!directToDdms) {
364        if (traceFd < 0) {
365            state->traceFile = fopen(traceFileName, "w");
366        } else {
367            state->traceFile = fdopen(traceFd, "w");
368        }
369        if (state->traceFile == NULL) {
370            int err = errno;
371            LOGE("Unable to open trace file '%s': %s\n",
372                traceFileName, strerror(err));
373            dvmThrowExceptionFmt("Ljava/lang/RuntimeException;",
374                "Unable to open trace file '%s': %s",
375                traceFileName, strerror(err));
376            goto fail;
377        }
378    }
379    memset(state->buf, (char)FILL_PATTERN, bufferSize);
380
381    state->directToDdms = directToDdms;
382    state->bufferSize = bufferSize;
383    state->overflow = false;
384
385    /*
386     * Enable alloc counts if we've been requested to do so.
387     */
388    state->flags = flags;
389    if ((flags & TRACE_ALLOC_COUNTS) != 0)
390        dvmStartAllocCounting();
391
392    /* reset our notion of the start time for all CPU threads */
393    resetCpuClockBase();
394
395    state->startWhen = getTimeInUsec();
396
397    /*
398     * Output the header.
399     */
400    memset(state->buf, 0, TRACE_HEADER_LEN);
401    storeIntLE(state->buf + 0, TRACE_MAGIC);
402    storeShortLE(state->buf + 4, TRACE_VERSION);
403    storeShortLE(state->buf + 6, TRACE_HEADER_LEN);
404    storeLongLE(state->buf + 8, state->startWhen);
405    state->curOffset = TRACE_HEADER_LEN;
406
407    MEM_BARRIER();
408
409    /*
410     * Set the "enabled" flag.  Once we do this, threads will wait to be
411     * signaled before exiting, so we have to make sure we wake them up.
412     */
413    state->traceEnabled = true;
414    dvmUnlockMutex(&state->startStopLock);
415    return;
416
417fail:
418    updateActiveProfilers(-1);
419    if (state->traceFile != NULL) {
420        fclose(state->traceFile);
421        state->traceFile = NULL;
422    }
423    if (state->buf != NULL) {
424        free(state->buf);
425        state->buf = NULL;
426    }
427    dvmUnlockMutex(&state->startStopLock);
428}
429
430/*
431 * Run through the data buffer and pull out the methods that were visited.
432 * Set a mark so that we know which ones to output.
433 */
434static void markTouchedMethods(int endOffset)
435{
436    u1* ptr = gDvm.methodTrace.buf + TRACE_HEADER_LEN;
437    u1* end = gDvm.methodTrace.buf + endOffset;
438    unsigned int methodVal;
439    Method* method;
440
441    while (ptr < end) {
442        methodVal = *(ptr+1) | (*(ptr+2) << 8) | (*(ptr+3) << 16)
443                    | (*(ptr+4) << 24);
444        method = (Method*) METHOD_ID(methodVal);
445
446        method->inProfile = true;
447        ptr += TRACE_REC_SIZE;
448    }
449}
450
451/*
452 * Compute the amount of overhead in a clock call, in nsec.
453 *
454 * This value is going to vary depending on what else is going on in the
455 * system.  When examined across several runs a pattern should emerge.
456 */
457static u4 getClockOverhead(void)
458{
459    u8 calStart, calElapsed;
460    int i;
461
462    calStart = getClock();
463    for (i = 1000 * 4; i > 0; i--) {
464        getClock();
465        getClock();
466        getClock();
467        getClock();
468        getClock();
469        getClock();
470        getClock();
471        getClock();
472    }
473
474    calElapsed = getClock() - calStart;
475    return (int) (calElapsed / (8*4));
476}
477
478/*
479 * Returns "true" if method tracing is currently active.
480 */
481bool dvmIsMethodTraceActive(void)
482{
483    const MethodTraceState* state = &gDvm.methodTrace;
484    return state->traceEnabled;
485}
486
487/*
488 * Stop method tracing.  We write the buffer to disk and generate a key
489 * file so we can interpret it.
490 */
491void dvmMethodTraceStop(void)
492{
493    MethodTraceState* state = &gDvm.methodTrace;
494    u8 elapsed;
495
496    /*
497     * We need this to prevent somebody from starting a new trace while
498     * we're in the process of stopping the old.
499     */
500    dvmLockMutex(&state->startStopLock);
501
502    if (!state->traceEnabled) {
503        /* somebody already stopped it, or it was never started */
504        LOGD("TRACE stop requested, but not running\n");
505        dvmUnlockMutex(&state->startStopLock);
506        return;
507    } else {
508        updateActiveProfilers(-1);
509    }
510
511    /* compute elapsed time */
512    elapsed = getTimeInUsec() - state->startWhen;
513
514    /*
515     * Globally disable it, and allow other threads to notice.  We want
516     * to stall here for at least as long as dvmMethodTraceAdd needs
517     * to finish.  There's no real risk though -- it will take a while to
518     * write the data to disk, and we don't clear the buffer pointer until
519     * after that completes.
520     */
521    state->traceEnabled = false;
522    MEM_BARRIER();
523    sched_yield();
524    usleep(250 * 1000);
525
526    if ((state->flags & TRACE_ALLOC_COUNTS) != 0)
527        dvmStopAllocCounting();
528
529    /*
530     * It's possible under some circumstances for a thread to have advanced
531     * the data pointer but not written the method value.  It's possible
532     * (though less likely) for the data pointer to be advanced, or partial
533     * data written, while we're doing work here.
534     *
535     * To avoid seeing partially-written data, we grab state->curOffset here,
536     * and use our local copy from here on.  We then scan through what's
537     * already written.  If we see the fill pattern in what should be the
538     * method pointer, we cut things off early.  (If we don't, we'll fail
539     * when we dereference the pointer.)
540     *
541     * There's a theoretical possibility of interrupting another thread
542     * after it has partially written the method pointer, in which case
543     * we'll likely crash when we dereference it.  The possibility of
544     * this actually happening should be at or near zero.  Fixing it
545     * completely could be done by writing the thread number last and
546     * using a sentinel value to indicate a partially-written record,
547     * but that requires memory barriers.
548     */
549    int finalCurOffset = state->curOffset;
550
551    if (finalCurOffset > TRACE_HEADER_LEN) {
552        u4 fillVal = METHOD_ID(FILL_PATTERN);
553        u1* scanPtr = state->buf + TRACE_HEADER_LEN;
554
555        while (scanPtr < state->buf + finalCurOffset) {
556            u4 methodVal = scanPtr[1] | (scanPtr[2] << 8) | (scanPtr[3] << 16)
557                        | (scanPtr[4] << 24);
558            if (METHOD_ID(methodVal) == fillVal) {
559                u1* scanBase = state->buf + TRACE_HEADER_LEN;
560                LOGW("Found unfilled record at %d (of %d)\n",
561                    (scanPtr - scanBase) / TRACE_REC_SIZE,
562                    (finalCurOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE);
563                finalCurOffset = scanPtr - state->buf;
564                break;
565            }
566
567            scanPtr += TRACE_REC_SIZE;
568        }
569    }
570
571    LOGI("TRACE STOPPED%s: writing %d records\n",
572        state->overflow ? " (NOTE: overflowed buffer)" : "",
573        (finalCurOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE);
574    if (gDvm.debuggerActive) {
575        LOGW("WARNING: a debugger is active; method-tracing results "
576             "will be skewed\n");
577    }
578
579    /*
580     * Do a quick calibration test to see how expensive our clock call is.
581     */
582    u4 clockNsec = getClockOverhead();
583
584    markTouchedMethods(finalCurOffset);
585
586    char* memStreamPtr;
587    size_t memStreamSize;
588    if (state->directToDdms) {
589        assert(state->traceFile == NULL);
590        state->traceFile = open_memstream(&memStreamPtr, &memStreamSize);
591        if (state->traceFile == NULL) {
592            /* not expected */
593            LOGE("Unable to open memstream\n");
594            dvmAbort();
595        }
596    }
597    assert(state->traceFile != NULL);
598
599    fprintf(state->traceFile, "%cversion\n", TOKEN_CHAR);
600    fprintf(state->traceFile, "%d\n", TRACE_VERSION);
601    fprintf(state->traceFile, "data-file-overflow=%s\n",
602        state->overflow ? "true" : "false");
603#if defined(HAVE_POSIX_CLOCKS)
604    fprintf(state->traceFile, "clock=thread-cpu\n");
605#else
606    fprintf(state->traceFile, "clock=global\n");
607#endif
608    fprintf(state->traceFile, "elapsed-time-usec=%llu\n", elapsed);
609    fprintf(state->traceFile, "num-method-calls=%d\n",
610        (finalCurOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE);
611    fprintf(state->traceFile, "clock-call-overhead-nsec=%d\n", clockNsec);
612    fprintf(state->traceFile, "vm=dalvik\n");
613    if ((state->flags & TRACE_ALLOC_COUNTS) != 0) {
614        fprintf(state->traceFile, "alloc-count=%d\n",
615            gDvm.allocProf.allocCount);
616        fprintf(state->traceFile, "alloc-size=%d\n",
617            gDvm.allocProf.allocSize);
618        fprintf(state->traceFile, "gc-count=%d\n",
619            gDvm.allocProf.gcCount);
620    }
621    fprintf(state->traceFile, "%cthreads\n", TOKEN_CHAR);
622    dumpThreadList(state->traceFile);
623    fprintf(state->traceFile, "%cmethods\n", TOKEN_CHAR);
624    dumpMethodList(state->traceFile);
625    fprintf(state->traceFile, "%cend\n", TOKEN_CHAR);
626
627    if (state->directToDdms) {
628        /*
629         * Data is in two places: memStreamPtr and state->buf.  Send
630         * the whole thing to DDMS, wrapped in an MPSE packet.
631         */
632        fflush(state->traceFile);
633
634        struct iovec iov[2];
635        iov[0].iov_base = memStreamPtr;
636        iov[0].iov_len = memStreamSize;
637        iov[1].iov_base = state->buf;
638        iov[1].iov_len = finalCurOffset;
639        dvmDbgDdmSendChunkV(CHUNK_TYPE("MPSE"), iov, 2);
640    } else {
641        /* append the profiling data */
642        if (fwrite(state->buf, finalCurOffset, 1, state->traceFile) != 1) {
643            int err = errno;
644            LOGE("trace fwrite(%d) failed, errno=%d\n", finalCurOffset, err);
645            dvmThrowExceptionFmt("Ljava/lang/RuntimeException;",
646                "Trace data write failed: %s", strerror(err));
647        }
648    }
649
650    /* done! */
651    free(state->buf);
652    state->buf = NULL;
653    fclose(state->traceFile);
654    state->traceFile = NULL;
655
656    /* wake any threads that were waiting for profiling to complete */
657    int cc = pthread_cond_broadcast(&state->threadExitCond);
658    assert(cc == 0);
659    dvmUnlockMutex(&state->startStopLock);
660}
661
662
663/*
664 * We just did something with a method.  Emit a record.
665 *
666 * Multiple threads may be banging on this all at once.  We use atomic ops
667 * rather than mutexes for speed.
668 */
669void dvmMethodTraceAdd(Thread* self, const Method* method, int action)
670{
671    MethodTraceState* state = &gDvm.methodTrace;
672    u4 clockDiff, methodVal;
673    int oldOffset, newOffset;
674    u1* ptr;
675
676    /*
677     * We can only access the per-thread CPU clock from within the
678     * thread, so we have to initialize the base time on the first use.
679     * (Looks like pthread_getcpuclockid(thread, &id) will do what we
680     * want, but it doesn't appear to be defined on the device.)
681     */
682    if (!self->cpuClockBaseSet) {
683        self->cpuClockBase = getClock();
684        self->cpuClockBaseSet = true;
685        //LOGI("thread base id=%d 0x%llx\n",
686        //    self->threadId, self->cpuClockBase);
687    }
688
689    /*
690     * Advance "curOffset" atomically.
691     */
692    do {
693        oldOffset = state->curOffset;
694        newOffset = oldOffset + TRACE_REC_SIZE;
695        if (newOffset > state->bufferSize) {
696            state->overflow = true;
697            return;
698        }
699    } while (!ATOMIC_CMP_SWAP(&state->curOffset, oldOffset, newOffset));
700
701    //assert(METHOD_ACTION((u4) method) == 0);
702
703    u8 now = getClock();
704    clockDiff = (u4) (now - self->cpuClockBase);
705
706    methodVal = METHOD_COMBINE((u4) method, action);
707
708    /*
709     * Write data into "oldOffset".
710     */
711    ptr = state->buf + oldOffset;
712    *ptr++ = self->threadId;
713    *ptr++ = (u1) methodVal;
714    *ptr++ = (u1) (methodVal >> 8);
715    *ptr++ = (u1) (methodVal >> 16);
716    *ptr++ = (u1) (methodVal >> 24);
717    *ptr++ = (u1) clockDiff;
718    *ptr++ = (u1) (clockDiff >> 8);
719    *ptr++ = (u1) (clockDiff >> 16);
720    *ptr++ = (u1) (clockDiff >> 24);
721}
722
723/*
724 * We just did something with a method.  Emit a record by setting a value
725 * in a magic memory location.
726 */
727void dvmEmitEmulatorTrace(const Method* method, int action)
728{
729#ifdef UPDATE_MAGIC_PAGE
730    /*
731     * We store the address of the Dalvik bytecodes to the memory-mapped
732     * trace page for normal Java methods.  We also trace calls to native
733     * functions by storing the address of the native function to the
734     * trace page.
735     * Abstract methods don't have any bytecodes, so we don't trace them.
736     * (Abstract methods are never called, but in Dalvik they can be
737     * because we do a "late trap" to a native method to generate the
738     * abstract method exception.)
739     */
740    if (dvmIsAbstractMethod(method))
741        return;
742
743    u4* pMagic = (u4*) gDvm.emulatorTracePage;
744    u4 addr;
745
746    if (dvmIsNativeMethod(method)) {
747        /*
748         * The "action" parameter is one of:
749         *   0 = ENTER
750         *   1 = EXIT
751         *   2 = UNROLL
752         * To help the trace tools reconstruct the runtime stack containing
753         * a mix of Java plus native methods, we add 4 to the action if this
754         * is a native method.
755         */
756        action += 4;
757
758        /*
759         * Get the address of the native function.
760         * This isn't the right address -- how do I get it?
761         * Fortunately, the trace tools can get by without the address, but
762         * it would be nice to fix this.
763         */
764         addr = (u4) method->nativeFunc;
765    } else {
766        /*
767         * The dexlist output shows the &DexCode.insns offset value, which
768         * is offset from the start of the base DEX header. Method.insns
769         * is the absolute address, effectively offset from the start of
770         * the optimized DEX header. We either need to return the
771         * optimized DEX base file address offset by the right amount, or
772         * take the "real" address and subtract off the size of the
773         * optimized DEX header.
774         *
775         * Would be nice to factor this out at dexlist time, but we can't count
776         * on having access to the correct optimized DEX file.
777         */
778        assert(method->insns != NULL);
779        const DexOptHeader* pOptHdr = method->clazz->pDvmDex->pDexFile->pOptHeader;
780        addr = (u4) method->insns - pOptHdr->dexOffset;
781    }
782
783    *(pMagic+action) = addr;
784    LOGVV("Set %p = 0x%08x (%s.%s)\n",
785        pMagic+action, addr, method->clazz->descriptor, method->name);
786#endif
787}
788
789/*
790 * The GC calls this when it's about to start.  We add a marker to the
791 * trace output so the tool can exclude the GC cost from the results.
792 */
793void dvmMethodTraceGCBegin(void)
794{
795    TRACE_METHOD_ENTER(dvmThreadSelf(), gDvm.methodTrace.gcMethod);
796}
797void dvmMethodTraceGCEnd(void)
798{
799    TRACE_METHOD_EXIT(dvmThreadSelf(), gDvm.methodTrace.gcMethod);
800}
801
802/*
803 * The class loader calls this when it's loading or initializing a class.
804 */
805void dvmMethodTraceClassPrepBegin(void)
806{
807    TRACE_METHOD_ENTER(dvmThreadSelf(), gDvm.methodTrace.classPrepMethod);
808}
809void dvmMethodTraceClassPrepEnd(void)
810{
811    TRACE_METHOD_EXIT(dvmThreadSelf(), gDvm.methodTrace.classPrepMethod);
812}
813
814
815/*
816 * Enable emulator trace info.
817 */
818void dvmEmulatorTraceStart(void)
819{
820    /* If we could not map the emulator trace page, then do not enable tracing */
821    if (gDvm.emulatorTracePage == NULL)
822        return;
823
824    updateActiveProfilers(1);
825
826    /* in theory we should make this an atomic inc; in practice not important */
827    gDvm.emulatorTraceEnableCount++;
828    if (gDvm.emulatorTraceEnableCount == 1)
829        LOGD("--- emulator method traces enabled\n");
830}
831
832/*
833 * Disable emulator trace info.
834 */
835void dvmEmulatorTraceStop(void)
836{
837    if (gDvm.emulatorTraceEnableCount == 0) {
838        LOGE("ERROR: emulator tracing not enabled\n");
839        return;
840    }
841    updateActiveProfilers(-1);
842    /* in theory we should make this an atomic inc; in practice not important */
843    gDvm.emulatorTraceEnableCount--;
844    if (gDvm.emulatorTraceEnableCount == 0)
845        LOGD("--- emulator method traces disabled\n");
846}
847
848
849/*
850 * Start instruction counting.
851 */
852void dvmStartInstructionCounting()
853{
854    updateActiveProfilers(1);
855    /* in theory we should make this an atomic inc; in practice not important */
856    gDvm.instructionCountEnableCount++;
857}
858
859/*
860 * Start instruction counting.
861 */
862void dvmStopInstructionCounting()
863{
864    if (gDvm.instructionCountEnableCount == 0) {
865        LOGE("ERROR: instruction counting not enabled\n");
866        dvmAbort();
867    }
868    updateActiveProfilers(-1);
869    gDvm.instructionCountEnableCount--;
870}
871
872
873/*
874 * Start alloc counting.  Note this doesn't affect the "active profilers"
875 * count, since the interpreter loop is not involved.
876 */
877void dvmStartAllocCounting(void)
878{
879    gDvm.allocProf.enabled = true;
880}
881
882/*
883 * Stop alloc counting.
884 */
885void dvmStopAllocCounting(void)
886{
887    gDvm.allocProf.enabled = false;
888}
889
890#endif /*WITH_PROFILER*/
891