2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * Android's method call profiling goodies.
21 #include <interp/InterpDefs.h>
33 #include <cutils/open_memstream.h>
35 #ifdef HAVE_ANDROID_OS
36 # define UPDATE_MAGIC_PAGE 1
50 * u8 start date/time in usec
54 * u4 method ID | method action
55 * u4 time delta since start, in usec
57 * 32 bits of microseconds is 70 minutes.
59 * All values are stored in little-endian order.
61 #define TRACE_REC_SIZE 9
62 #define TRACE_MAGIC 0x574f4c53
63 #define TRACE_HEADER_LEN 32
65 #define FILL_PATTERN 0xeeeeeeee
69 * Get the wall-clock date/time, in usec.
71 static inline u8 getTimeInUsec()
75 gettimeofday(&tv, NULL);
76 return tv.tv_sec * 1000000LL + tv.tv_usec;
80 * Get the current time, in microseconds.
82 * This can mean one of two things. In "global clock" mode, we get the
83 * same time across all threads. If we use CLOCK_THREAD_CPUTIME_ID, we
84 * get a per-thread CPU usage timer. The latter is better, but a bit
85 * more complicated to implement.
87 static inline u8 getClock()
89 #if defined(HAVE_POSIX_CLOCKS)
90 if (!gDvm.profilerWallClock) {
93 clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);
94 if (!(tm.tv_nsec >= 0 && tm.tv_nsec < 1*1000*1000*1000)) {
95 LOGE("bad nsec: %ld", tm.tv_nsec);
99 return tm.tv_sec * 1000000LL + tm.tv_nsec / 1000;
105 gettimeofday(&tv, NULL);
106 return tv.tv_sec * 1000000LL + tv.tv_usec;
111 * Write little-endian data.
113 static inline void storeShortLE(u1* buf, u2 val)
116 *buf++ = (u1) (val >> 8);
118 static inline void storeIntLE(u1* buf, u4 val)
121 *buf++ = (u1) (val >> 8);
122 *buf++ = (u1) (val >> 16);
123 *buf++ = (u1) (val >> 24);
125 static inline void storeLongLE(u1* buf, u8 val)
128 *buf++ = (u1) (val >> 8);
129 *buf++ = (u1) (val >> 16);
130 *buf++ = (u1) (val >> 24);
131 *buf++ = (u1) (val >> 32);
132 *buf++ = (u1) (val >> 40);
133 *buf++ = (u1) (val >> 48);
134 *buf++ = (u1) (val >> 56);
140 bool dvmProfilingStartup()
143 * Initialize "dmtrace" method profiling.
145 memset(&gDvm.methodTrace, 0, sizeof(gDvm.methodTrace));
146 dvmInitMutex(&gDvm.methodTrace.startStopLock);
147 pthread_cond_init(&gDvm.methodTrace.threadExitCond, NULL);
149 assert(!dvmCheckException(dvmThreadSelf()));
152 * Allocate storage for instruction counters.
154 gDvm.executedInstrCounts = (int*) malloc(kNumPackedOpcodes * sizeof(int));
155 if (gDvm.executedInstrCounts == NULL)
157 memset(gDvm.executedInstrCounts, 0, kNumPackedOpcodes * sizeof(int));
159 #ifdef UPDATE_MAGIC_PAGE
161 * If we're running on the emulator, there's a magic page into which
162 * we can put interpreted method information. This allows interpreted
163 * methods to show up in the emulator's code traces.
165 * We could key this off of the "ro.kernel.qemu" property, but there's
166 * no real harm in doing this on a real device.
168 int fd = open("/dev/qemu_trace", O_RDWR);
170 LOGV("Unable to open /dev/qemu_trace");
172 gDvm.emulatorTracePage = mmap(0, SYSTEM_PAGE_SIZE, PROT_READ|PROT_WRITE,
175 if (gDvm.emulatorTracePage == MAP_FAILED) {
176 LOGE("Unable to mmap /dev/qemu_trace");
177 gDvm.emulatorTracePage = NULL;
179 *(u4*) gDvm.emulatorTracePage = 0;
183 assert(gDvm.emulatorTracePage == NULL);
190 * Free up profiling resources.
192 void dvmProfilingShutdown()
194 #ifdef UPDATE_MAGIC_PAGE
195 if (gDvm.emulatorTracePage != NULL)
196 munmap(gDvm.emulatorTracePage, SYSTEM_PAGE_SIZE);
198 free(gDvm.executedInstrCounts);
202 * Update the set of active profilers
204 static void updateActiveProfilers(ExecutionSubModes newMode, bool enable)
206 int oldValue, newValue;
208 // Update global count
210 oldValue = gDvm.activeProfilers;
211 newValue = oldValue + (enable ? 1 : -1);
213 LOGE("Can't have %d active profilers", newValue);
216 } while (android_atomic_release_cas(oldValue, newValue,
217 &gDvm.activeProfilers) != 0);
221 dvmEnableAllSubMode(newMode);
223 dvmDisableAllSubMode(newMode);
226 #if defined(WITH_JIT)
227 dvmCompilerUpdateGlobalState();
230 LOGD("+++ active profiler count now %d", newValue);
235 * Reset the "cpuClockBase" field in all threads.
237 static void resetCpuClockBase()
241 dvmLockThreadList(NULL);
242 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
243 thread->cpuClockBaseSet = false;
244 thread->cpuClockBase = 0;
246 dvmUnlockThreadList();
250 * Dump the thread list to the specified file.
252 static void dumpThreadList(FILE* fp) {
253 dvmLockThreadList(NULL);
254 for (Thread* thread = gDvm.threadList; thread != NULL; thread = thread->next) {
255 std::string threadName(dvmGetThreadName(thread));
256 fprintf(fp, "%d\t%s\n", thread->threadId, threadName.c_str());
258 dvmUnlockThreadList();
262 * This is a dvmHashForeach callback.
264 static int dumpMarkedMethods(void* vclazz, void* vfp)
266 DexStringCache stringCache;
267 ClassObject* clazz = (ClassObject*) vclazz;
268 FILE* fp = (FILE*) vfp;
273 dexStringCacheInit(&stringCache);
275 for (i = 0; i < clazz->virtualMethodCount; i++) {
276 meth = &clazz->virtualMethods[i];
277 if (meth->inProfile) {
278 name = dvmDescriptorToName(meth->clazz->descriptor);
279 fprintf(fp, "0x%08x\t%s\t%s\t%s\t%s\t%d\n", (int) meth,
281 dexProtoGetMethodDescriptor(&meth->prototype, &stringCache),
282 dvmGetMethodSourceFile(meth), dvmLineNumFromPC(meth, 0));
283 meth->inProfile = false;
288 for (i = 0; i < clazz->directMethodCount; i++) {
289 meth = &clazz->directMethods[i];
290 if (meth->inProfile) {
291 name = dvmDescriptorToName(meth->clazz->descriptor);
292 fprintf(fp, "0x%08x\t%s\t%s\t%s\t%s\t%d\n", (int) meth,
294 dexProtoGetMethodDescriptor(&meth->prototype, &stringCache),
295 dvmGetMethodSourceFile(meth), dvmLineNumFromPC(meth, 0));
296 meth->inProfile = false;
301 dexStringCacheRelease(&stringCache);
307 * Dump the list of "marked" methods to the specified file.
309 static void dumpMethodList(FILE* fp)
311 dvmHashTableLock(gDvm.loadedClasses);
312 dvmHashForeach(gDvm.loadedClasses, dumpMarkedMethods, (void*) fp);
313 dvmHashTableUnlock(gDvm.loadedClasses);
317 * Start method tracing. Method tracing is global to the VM (i.e. we
318 * trace all threads).
320 * This opens the output file (if an already open fd has not been supplied,
321 * and we're not going direct to DDMS) and allocates the data buffer. This
322 * takes ownership of the file descriptor, closing it on completion.
324 * On failure, we throw an exception and return.
326 void dvmMethodTraceStart(const char* traceFileName, int traceFd, int bufferSize,
327 int flags, bool directToDdms)
329 MethodTraceState* state = &gDvm.methodTrace;
331 assert(bufferSize > 0);
333 dvmLockMutex(&state->startStopLock);
334 while (state->traceEnabled != 0) {
335 LOGI("TRACE start requested, but already in progress; stopping");
336 dvmUnlockMutex(&state->startStopLock);
337 dvmMethodTraceStop();
338 dvmLockMutex(&state->startStopLock);
341 * ENHANCEMENT: To trace just a single thread, modify the
342 * following to take a Thread* argument, and set the appropriate
343 * interpBreak flags only on the target thread.
345 updateActiveProfilers(kSubModeMethodTrace, true);
346 LOGI("TRACE STARTED: '%s' %dKB", traceFileName, bufferSize / 1024);
349 * Allocate storage and open files.
351 * We don't need to initialize the buffer, but doing so might remove
352 * some fault overhead if the pages aren't mapped until touched.
354 state->buf = (u1*) malloc(bufferSize);
355 if (state->buf == NULL) {
356 dvmThrowInternalError("buffer alloc failed");
361 state->traceFile = fopen(traceFileName, "w");
363 state->traceFile = fdopen(traceFd, "w");
365 if (state->traceFile == NULL) {
367 LOGE("Unable to open trace file '%s': %s",
368 traceFileName, strerror(err));
369 dvmThrowExceptionFmt(gDvm.exRuntimeException,
370 "Unable to open trace file '%s': %s",
371 traceFileName, strerror(err));
376 memset(state->buf, (char)FILL_PATTERN, bufferSize);
378 state->directToDdms = directToDdms;
379 state->bufferSize = bufferSize;
380 state->overflow = false;
383 * Enable alloc counts if we've been requested to do so.
385 state->flags = flags;
386 if ((flags & TRACE_ALLOC_COUNTS) != 0)
387 dvmStartAllocCounting();
389 /* reset our notion of the start time for all CPU threads */
392 state->startWhen = getTimeInUsec();
397 memset(state->buf, 0, TRACE_HEADER_LEN);
398 storeIntLE(state->buf + 0, TRACE_MAGIC);
399 storeShortLE(state->buf + 4, TRACE_VERSION);
400 storeShortLE(state->buf + 6, TRACE_HEADER_LEN);
401 storeLongLE(state->buf + 8, state->startWhen);
402 state->curOffset = TRACE_HEADER_LEN;
405 * Set the "enabled" flag. Once we do this, threads will wait to be
406 * signaled before exiting, so we have to make sure we wake them up.
408 android_atomic_release_store(true, &state->traceEnabled);
409 dvmUnlockMutex(&state->startStopLock);
413 updateActiveProfilers(kSubModeMethodTrace, false);
414 if (state->traceFile != NULL) {
415 fclose(state->traceFile);
416 state->traceFile = NULL;
418 if (state->buf != NULL) {
424 dvmUnlockMutex(&state->startStopLock);
428 * Run through the data buffer and pull out the methods that were visited.
429 * Set a mark so that we know which ones to output.
431 static void markTouchedMethods(int endOffset)
433 u1* ptr = gDvm.methodTrace.buf + TRACE_HEADER_LEN;
434 u1* end = gDvm.methodTrace.buf + endOffset;
435 unsigned int methodVal;
439 methodVal = *(ptr+1) | (*(ptr+2) << 8) | (*(ptr+3) << 16)
441 method = (Method*) METHOD_ID(methodVal);
443 method->inProfile = true;
444 ptr += TRACE_REC_SIZE;
449 * Compute the amount of overhead in a clock call, in nsec.
451 * This value is going to vary depending on what else is going on in the
452 * system. When examined across several runs a pattern should emerge.
454 static u4 getClockOverhead()
456 u8 calStart, calElapsed;
459 calStart = getClock();
460 for (i = 1000 * 4; i > 0; i--) {
471 calElapsed = getClock() - calStart;
472 return (int) (calElapsed / (8*4));
476 * Returns "true" if method tracing is currently active.
478 bool dvmIsMethodTraceActive()
480 const MethodTraceState* state = &gDvm.methodTrace;
481 return state->traceEnabled;
485 * Stop method tracing. We write the buffer to disk and generate a key
486 * file so we can interpret it.
488 void dvmMethodTraceStop()
490 MethodTraceState* state = &gDvm.methodTrace;
494 * We need this to prevent somebody from starting a new trace while
495 * we're in the process of stopping the old.
497 dvmLockMutex(&state->startStopLock);
499 if (!state->traceEnabled) {
500 /* somebody already stopped it, or it was never started */
501 LOGD("TRACE stop requested, but not running");
502 dvmUnlockMutex(&state->startStopLock);
505 updateActiveProfilers(kSubModeMethodTrace, false);
508 /* compute elapsed time */
509 elapsed = getTimeInUsec() - state->startWhen;
512 * Globally disable it, and allow other threads to notice. We want
513 * to stall here for at least as long as dvmMethodTraceAdd needs
514 * to finish. There's no real risk though -- it will take a while to
515 * write the data to disk, and we don't clear the buffer pointer until
516 * after that completes.
518 state->traceEnabled = false;
519 ANDROID_MEMBAR_FULL();
523 if ((state->flags & TRACE_ALLOC_COUNTS) != 0)
524 dvmStopAllocCounting();
527 * It's possible under some circumstances for a thread to have advanced
528 * the data pointer but not written the method value. It's possible
529 * (though less likely) for the data pointer to be advanced, or partial
530 * data written, while we're doing work here.
532 * To avoid seeing partially-written data, we grab state->curOffset here,
533 * and use our local copy from here on. We then scan through what's
534 * already written. If we see the fill pattern in what should be the
535 * method pointer, we cut things off early. (If we don't, we'll fail
536 * when we dereference the pointer.)
538 * There's a theoretical possibility of interrupting another thread
539 * after it has partially written the method pointer, in which case
540 * we'll likely crash when we dereference it. The possibility of
541 * this actually happening should be at or near zero. Fixing it
542 * completely could be done by writing the thread number last and
543 * using a sentinel value to indicate a partially-written record,
544 * but that requires memory barriers.
546 int finalCurOffset = state->curOffset;
548 if (finalCurOffset > TRACE_HEADER_LEN) {
549 u4 fillVal = METHOD_ID(FILL_PATTERN);
550 u1* scanPtr = state->buf + TRACE_HEADER_LEN;
552 while (scanPtr < state->buf + finalCurOffset) {
553 u4 methodVal = scanPtr[1] | (scanPtr[2] << 8) | (scanPtr[3] << 16)
554 | (scanPtr[4] << 24);
555 if (METHOD_ID(methodVal) == fillVal) {
556 u1* scanBase = state->buf + TRACE_HEADER_LEN;
557 LOGW("Found unfilled record at %d (of %d)",
558 (scanPtr - scanBase) / TRACE_REC_SIZE,
559 (finalCurOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE);
560 finalCurOffset = scanPtr - state->buf;
564 scanPtr += TRACE_REC_SIZE;
568 LOGI("TRACE STOPPED%s: writing %d records",
569 state->overflow ? " (NOTE: overflowed buffer)" : "",
570 (finalCurOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE);
571 if (gDvm.debuggerActive) {
572 LOGW("WARNING: a debugger is active; method-tracing results "
577 * Do a quick calibration test to see how expensive our clock call is.
579 u4 clockNsec = getClockOverhead();
581 markTouchedMethods(finalCurOffset);
584 size_t memStreamSize;
585 if (state->directToDdms) {
586 assert(state->traceFile == NULL);
587 state->traceFile = open_memstream(&memStreamPtr, &memStreamSize);
588 if (state->traceFile == NULL) {
590 LOGE("Unable to open memstream");
594 assert(state->traceFile != NULL);
596 fprintf(state->traceFile, "%cversion\n", TOKEN_CHAR);
597 fprintf(state->traceFile, "%d\n", TRACE_VERSION);
598 fprintf(state->traceFile, "data-file-overflow=%s\n",
599 state->overflow ? "true" : "false");
600 #if defined(HAVE_POSIX_CLOCKS)
601 if (!gDvm.profilerWallClock) {
602 fprintf(state->traceFile, "clock=thread-cpu\n");
606 fprintf(state->traceFile, "clock=wall\n");
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);
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);
627 if (state->directToDdms) {
629 * Data is in two places: memStreamPtr and state->buf. Send
630 * the whole thing to DDMS, wrapped in an MPSE packet.
632 fflush(state->traceFile);
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);
641 /* append the profiling data */
642 if (fwrite(state->buf, finalCurOffset, 1, state->traceFile) != 1) {
644 LOGE("trace fwrite(%d) failed: %s",
645 finalCurOffset, strerror(err));
646 dvmThrowExceptionFmt(gDvm.exRuntimeException,
647 "Trace data write failed: %s", strerror(err));
654 fclose(state->traceFile);
655 state->traceFile = NULL;
657 /* wake any threads that were waiting for profiling to complete */
658 dvmBroadcastCond(&state->threadExitCond);
659 dvmUnlockMutex(&state->startStopLock);
663 * We just did something with a method. Emit a record.
665 * Multiple threads may be banging on this all at once. We use atomic ops
666 * rather than mutexes for speed.
668 void dvmMethodTraceAdd(Thread* self, const Method* method, int action)
670 MethodTraceState* state = &gDvm.methodTrace;
671 u4 clockDiff, methodVal;
672 int oldOffset, newOffset;
675 assert(method != NULL);
678 * We can only access the per-thread CPU clock from within the
679 * thread, so we have to initialize the base time on the first use.
680 * (Looks like pthread_getcpuclockid(thread, &id) will do what we
681 * want, but it doesn't appear to be defined on the device.)
683 if (!self->cpuClockBaseSet) {
684 self->cpuClockBase = getClock();
685 self->cpuClockBaseSet = true;
686 //LOGI("thread base id=%d 0x%llx",
687 // self->threadId, self->cpuClockBase);
691 * Advance "curOffset" atomically.
694 oldOffset = state->curOffset;
695 newOffset = oldOffset + TRACE_REC_SIZE;
696 if (newOffset > state->bufferSize) {
697 state->overflow = true;
700 } while (android_atomic_release_cas(oldOffset, newOffset,
701 &state->curOffset) != 0);
703 //assert(METHOD_ACTION((u4) method) == 0);
706 clockDiff = (u4) (now - self->cpuClockBase);
708 methodVal = METHOD_COMBINE((u4) method, action);
711 * Write data into "oldOffset".
713 ptr = state->buf + oldOffset;
714 *ptr++ = self->threadId;
715 *ptr++ = (u1) methodVal;
716 *ptr++ = (u1) (methodVal >> 8);
717 *ptr++ = (u1) (methodVal >> 16);
718 *ptr++ = (u1) (methodVal >> 24);
719 *ptr++ = (u1) clockDiff;
720 *ptr++ = (u1) (clockDiff >> 8);
721 *ptr++ = (u1) (clockDiff >> 16);
722 *ptr++ = (u1) (clockDiff >> 24);
727 * Register the METHOD_TRACE_ENTER action for the fast interpreter and
730 void dvmFastMethodTraceEnter(const Method* method, Thread* self)
732 if (self->interpBreak.ctl.subMode & kSubModeMethodTrace) {
733 dvmMethodTraceAdd(self, method, METHOD_TRACE_ENTER);
738 * Register the METHOD_TRACE_EXIT action for the fast interpreter and
739 * JIT'ed code for methods. The about-to-return callee method can be
740 * retrieved from self->interpSave.method.
742 void dvmFastMethodTraceExit(Thread* self)
744 if (self->interpBreak.ctl.subMode & kSubModeMethodTrace) {
745 dvmMethodTraceAdd(self, self->interpSave.method,
751 * Register the METHOD_TRACE_EXIT action for the fast interpreter and
752 * JIT'ed code for JNI methods. The about-to-return JNI callee method is passed
753 * in explicitly. Also used for inline-execute.
755 void dvmFastNativeMethodTraceExit(const Method* method, Thread* self)
757 if (self->interpBreak.ctl.subMode & kSubModeMethodTrace) {
758 dvmMethodTraceAdd(self, method, METHOD_TRACE_EXIT);
763 * We just did something with a method. Emit a record by setting a value
764 * in a magic memory location.
766 void dvmEmitEmulatorTrace(const Method* method, int action)
768 #ifdef UPDATE_MAGIC_PAGE
770 * We store the address of the Dalvik bytecodes to the memory-mapped
771 * trace page for normal methods. We also trace calls to native
772 * functions by storing the address of the native function to the
774 * Abstract methods don't have any bytecodes, so we don't trace them.
775 * (Abstract methods are never called, but in Dalvik they can be
776 * because we do a "late trap" to a native method to generate the
777 * abstract method exception.)
779 if (dvmIsAbstractMethod(method))
782 u4* pMagic = (u4*) gDvm.emulatorTracePage;
785 if (dvmIsNativeMethod(method)) {
787 * The "action" parameter is one of:
791 * To help the trace tools reconstruct the runtime stack containing
792 * a mix of normal plus native methods, we add 4 to the action if this
793 * is a native method.
798 * Get the address of the native function.
799 * This isn't the right address -- how do I get it?
800 * Fortunately, the trace tools can get by without the address, but
801 * it would be nice to fix this.
803 addr = (u4) method->nativeFunc;
806 * The dexlist output shows the &DexCode.insns offset value, which
807 * is offset from the start of the base DEX header. Method.insns
808 * is the absolute address, effectively offset from the start of
809 * the optimized DEX header. We either need to return the
810 * optimized DEX base file address offset by the right amount, or
811 * take the "real" address and subtract off the size of the
812 * optimized DEX header.
814 * Would be nice to factor this out at dexlist time, but we can't count
815 * on having access to the correct optimized DEX file.
817 assert(method->insns != NULL);
818 const DexOptHeader* pOptHdr = method->clazz->pDvmDex->pDexFile->pOptHeader;
819 addr = (u4) method->insns - pOptHdr->dexOffset;
822 *(pMagic+action) = addr;
823 LOGVV("Set %p = 0x%08x (%s.%s)",
824 pMagic+action, addr, method->clazz->descriptor, method->name);
829 * The GC calls this when it's about to start. We add a marker to the
830 * trace output so the tool can exclude the GC cost from the results.
832 void dvmMethodTraceGCBegin()
834 TRACE_METHOD_ENTER(dvmThreadSelf(), gDvm.methodTraceGcMethod);
836 void dvmMethodTraceGCEnd()
838 TRACE_METHOD_EXIT(dvmThreadSelf(), gDvm.methodTraceGcMethod);
842 * The class loader calls this when it's loading or initializing a class.
844 void dvmMethodTraceClassPrepBegin()
846 TRACE_METHOD_ENTER(dvmThreadSelf(), gDvm.methodTraceClassPrepMethod);
848 void dvmMethodTraceClassPrepEnd()
850 TRACE_METHOD_EXIT(dvmThreadSelf(), gDvm.methodTraceClassPrepMethod);
855 * Enable emulator trace info.
857 void dvmEmulatorTraceStart()
859 /* If we could not map the emulator trace page, then do not enable tracing */
860 if (gDvm.emulatorTracePage == NULL)
863 /* in theory we should make this an atomic inc; in practice not important */
864 gDvm.emulatorTraceEnableCount++;
865 if (gDvm.emulatorTraceEnableCount == 1)
866 LOGD("--- emulator method traces enabled");
867 updateActiveProfilers(kSubModeEmulatorTrace, true);
871 * Disable emulator trace info.
873 void dvmEmulatorTraceStop()
875 if (gDvm.emulatorTraceEnableCount == 0) {
876 LOGE("ERROR: emulator tracing not enabled");
879 /* in theory we should make this an atomic inc; in practice not important */
880 gDvm.emulatorTraceEnableCount--;
881 if (gDvm.emulatorTraceEnableCount == 0)
882 LOGD("--- emulator method traces disabled");
883 updateActiveProfilers(kSubModeEmulatorTrace,
884 (gDvm.emulatorTraceEnableCount != 0));
889 * Start instruction counting.
891 void dvmStartInstructionCounting()
893 /* in theory we should make this an atomic inc; in practice not important */
894 gDvm.instructionCountEnableCount++;
895 updateActiveProfilers(kSubModeInstCounting, true);
899 * Stop instruction counting.
901 void dvmStopInstructionCounting()
903 if (gDvm.instructionCountEnableCount == 0) {
904 LOGE("ERROR: instruction counting not enabled");
907 gDvm.instructionCountEnableCount--;
908 updateActiveProfilers(kSubModeInstCounting,
909 (gDvm.instructionCountEnableCount != 0));
914 * Start alloc counting. Note this doesn't affect the "active profilers"
915 * count, since the interpreter loop is not involved.
917 void dvmStartAllocCounting()
919 gDvm.allocProf.enabled = true;
923 * Stop alloc counting.
925 void dvmStopAllocCounting()
927 gDvm.allocProf.enabled = false;