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