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