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