blob: 6463dab2eb6c2caf7eb3d52e1f88c88c4180a4c5 [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
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 */
Andy McFadden8c880b92009-03-24 18:15:56 -070016
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080017/*
18 * Android's method call profiling goodies.
19 */
20#include "Dalvik.h"
21
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080022#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>
Jack Veenstrac3b182a2009-05-05 11:29:51 -070030#include <fcntl.h>
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080031
Andy McFadden01718122010-01-22 16:36:30 -080032#include <cutils/open_memstream.h>
33
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080034#ifdef HAVE_ANDROID_OS
35# define UPDATE_MAGIC_PAGE 1
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080036#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
Andy McFadden183ce582009-06-04 17:00:58 -070064#define FILL_PATTERN 0xeeeeeeee
65
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080066
67/*
68 * Get the wall-clock date/time, in usec.
69 */
70static 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 */
86static inline u8 getClock()
87{
88#if defined(HAVE_POSIX_CLOCKS)
Andy McFaddenea414342010-08-25 12:05:44 -070089 if (!gDvm.profilerWallClock) {
90 struct timespec tm;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080091
Andy McFaddenea414342010-08-25 12:05:44 -070092 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 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080097
Andy McFaddenea414342010-08-25 12:05:44 -070098 return tm.tv_sec * 1000000LL + tm.tv_nsec / 1000;
99 } else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800100#endif
Andy McFaddenea414342010-08-25 12:05:44 -0700101 {
102 struct timeval tv;
103
104 gettimeofday(&tv, NULL);
105 return tv.tv_sec * 1000000LL + tv.tv_usec;
106 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800107}
108
109/*
110 * Write little-endian data.
111 */
112static inline void storeShortLE(u1* buf, u2 val)
113{
114 *buf++ = (u1) val;
115 *buf++ = (u1) (val >> 8);
116}
117static inline void storeIntLE(u1* buf, u4 val)
118{
119 *buf++ = (u1) val;
120 *buf++ = (u1) (val >> 8);
121 *buf++ = (u1) (val >> 16);
122 *buf++ = (u1) (val >> 24);
123}
124static inline void storeLongLE(u1* buf, u8 val)
125{
126 *buf++ = (u1) val;
127 *buf++ = (u1) (val >> 8);
128 *buf++ = (u1) (val >> 16);
129 *buf++ = (u1) (val >> 24);
130 *buf++ = (u1) (val >> 32);
131 *buf++ = (u1) (val >> 40);
132 *buf++ = (u1) (val >> 48);
133 *buf++ = (u1) (val >> 56);
134}
135
136/*
137 * Boot-time init.
138 */
139bool dvmProfilingStartup(void)
140{
141 /*
142 * Initialize "dmtrace" method profiling.
143 */
144 memset(&gDvm.methodTrace, 0, sizeof(gDvm.methodTrace));
145 dvmInitMutex(&gDvm.methodTrace.startStopLock);
146 pthread_cond_init(&gDvm.methodTrace.threadExitCond, NULL);
147
148 ClassObject* clazz =
149 dvmFindClassNoInit("Ldalvik/system/VMDebug;", NULL);
150 assert(clazz != NULL);
151 gDvm.methodTrace.gcMethod =
152 dvmFindDirectMethodByDescriptor(clazz, "startGC", "()V");
153 gDvm.methodTrace.classPrepMethod =
154 dvmFindDirectMethodByDescriptor(clazz, "startClassPrep", "()V");
155 if (gDvm.methodTrace.gcMethod == NULL ||
156 gDvm.methodTrace.classPrepMethod == NULL)
157 {
158 LOGE("Unable to find startGC or startClassPrep\n");
159 return false;
160 }
161
162 assert(!dvmCheckException(dvmThreadSelf()));
163
164 /*
165 * Allocate storage for instruction counters.
166 */
167 gDvm.executedInstrCounts = (int*) malloc(kNumDalvikInstructions * sizeof(int));
168 if (gDvm.executedInstrCounts == NULL)
169 return false;
170 memset(gDvm.executedInstrCounts, 0, kNumDalvikInstructions * sizeof(int));
171
172#ifdef UPDATE_MAGIC_PAGE
173 /*
174 * If we're running on the emulator, there's a magic page into which
175 * we can put interpreted method information. This allows interpreted
176 * methods to show up in the emulator's code traces.
177 *
178 * We could key this off of the "ro.kernel.qemu" property, but there's
179 * no real harm in doing this on a real device.
180 */
Jack Veenstrac3b182a2009-05-05 11:29:51 -0700181 int fd = open("/dev/qemu_trace", O_RDWR);
182 if (fd < 0) {
183 LOGV("Unable to open /dev/qemu_trace\n");
184 } else {
Andy McFadden96516932009-10-28 17:39:02 -0700185 gDvm.emulatorTracePage = mmap(0, SYSTEM_PAGE_SIZE, PROT_READ|PROT_WRITE,
Jack Veenstrac3b182a2009-05-05 11:29:51 -0700186 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 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800194 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800195#else
196 assert(gDvm.emulatorTracePage == NULL);
197#endif
198
199 return true;
200}
201
202/*
203 * Free up profiling resources.
204 */
205void dvmProfilingShutdown(void)
206{
207#ifdef UPDATE_MAGIC_PAGE
208 if (gDvm.emulatorTracePage != NULL)
Andy McFadden96516932009-10-28 17:39:02 -0700209 munmap(gDvm.emulatorTracePage, SYSTEM_PAGE_SIZE);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800210#endif
211 free(gDvm.executedInstrCounts);
212}
213
214/*
215 * Update the "active profilers" count.
216 *
217 * "count" should be +1 or -1.
218 */
219static void updateActiveProfilers(int count)
220{
221 int oldValue, newValue;
222
223 do {
224 oldValue = gDvm.activeProfilers;
225 newValue = oldValue + count;
226 if (newValue < 0) {
227 LOGE("Can't have %d active profilers\n", newValue);
228 dvmAbort();
229 }
Andy McFadden6e10b9a2010-06-14 15:24:39 -0700230 } while (android_atomic_release_cas(oldValue, newValue,
231 &gDvm.activeProfilers) != 0);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800232
233 LOGD("+++ active profiler count now %d\n", newValue);
Bill Buzbee06bb8392010-01-31 18:53:15 -0800234#if defined(WITH_JIT)
235 dvmCompilerStateRefresh();
236#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800237}
238
239
240/*
241 * Reset the "cpuClockBase" field in all threads.
242 */
243static 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 */
258static 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 */
275static 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;
Carl Shapiroe3c01da2010-05-20 22:54:18 -0700282 int i;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800283
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 */
320static void dumpMethodList(FILE* fp)
321{
322 dvmHashTableLock(gDvm.loadedClasses);
323 dvmHashForeach(gDvm.loadedClasses, dumpMarkedMethods, (void*) fp);
324 dvmHashTableUnlock(gDvm.loadedClasses);
325}
326
327/*
Andy McFadden01718122010-01-22 16:36:30 -0800328 * Start method tracing. Method tracing is global to the VM (i.e. we
329 * trace all threads).
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800330 *
Andy McFadden01718122010-01-22 16:36:30 -0800331 * This opens the output file (if an already open fd has not been supplied,
Andy McFadden4b851a72010-07-09 16:50:05 -0700332 * 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.
Andy McFadden01718122010-01-22 16:36:30 -0800334 *
335 * On failure, we throw an exception and return.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800336 */
Dianne Hackborn0f0ae022009-06-23 19:21:10 -0700337void dvmMethodTraceStart(const char* traceFileName, int traceFd, int bufferSize,
Andy McFadden01718122010-01-22 16:36:30 -0800338 int flags, bool directToDdms)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800339{
340 MethodTraceState* state = &gDvm.methodTrace;
341
342 assert(bufferSize > 0);
343
Andy McFadden01718122010-01-22 16:36:30 -0800344 dvmLockMutex(&state->startStopLock);
345 while (state->traceEnabled != 0) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800346 LOGI("TRACE start requested, but already in progress; stopping\n");
Andy McFadden01718122010-01-22 16:36:30 -0800347 dvmUnlockMutex(&state->startStopLock);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800348 dvmMethodTraceStop();
Andy McFadden01718122010-01-22 16:36:30 -0800349 dvmLockMutex(&state->startStopLock);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800350 }
351 updateActiveProfilers(1);
Andy McFadden01718122010-01-22 16:36:30 -0800352 LOGI("TRACE STARTED: '%s' %dKB\n", traceFileName, bufferSize / 1024);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800353
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 }
Andy McFadden01718122010-01-22 16:36:30 -0800365 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 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800380 }
Andy McFadden4b851a72010-07-09 16:50:05 -0700381 traceFd = -1;
Andy McFadden183ce582009-06-04 17:00:58 -0700382 memset(state->buf, (char)FILL_PATTERN, bufferSize);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800383
Andy McFadden01718122010-01-22 16:36:30 -0800384 state->directToDdms = directToDdms;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800385 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
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800410 /*
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 */
Andy McFaddenfc3d3162010-08-05 14:34:26 -0700414 android_atomic_release_store(true, &state->traceEnabled);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800415 dvmUnlockMutex(&state->startStopLock);
416 return;
417
418fail:
Andy McFadden8c880b92009-03-24 18:15:56 -0700419 updateActiveProfilers(-1);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800420 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 }
Andy McFadden4b851a72010-07-09 16:50:05 -0700428 if (traceFd >= 0)
429 close(traceFd);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800430 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 */
Andy McFadden183ce582009-06-04 17:00:58 -0700437static void markTouchedMethods(int endOffset)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800438{
439 u1* ptr = gDvm.methodTrace.buf + TRACE_HEADER_LEN;
Andy McFadden183ce582009-06-04 17:00:58 -0700440 u1* end = gDvm.methodTrace.buf + endOffset;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800441 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 */
460static 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/*
Andy McFadden8c880b92009-03-24 18:15:56 -0700482 * Returns "true" if method tracing is currently active.
483 */
484bool dvmIsMethodTraceActive(void)
485{
486 const MethodTraceState* state = &gDvm.methodTrace;
487 return state->traceEnabled;
488}
489
490/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800491 * Stop method tracing. We write the buffer to disk and generate a key
492 * file so we can interpret it.
493 */
494void 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 */
Andy McFadden8c880b92009-03-24 18:15:56 -0700507 LOGD("TRACE stop requested, but not running\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800508 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;
Andy McFadden6e10b9a2010-06-14 15:24:39 -0700525 ANDROID_MEMBAR_FULL();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800526 sched_yield();
Andy McFadden183ce582009-06-04 17:00:58 -0700527 usleep(250 * 1000);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800528
529 if ((state->flags & TRACE_ALLOC_COUNTS) != 0)
530 dvmStopAllocCounting();
531
Andy McFadden183ce582009-06-04 17:00:58 -0700532 /*
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
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800574 LOGI("TRACE STOPPED%s: writing %d records\n",
575 state->overflow ? " (NOTE: overflowed buffer)" : "",
Andy McFadden183ce582009-06-04 17:00:58 -0700576 (finalCurOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800577 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
Andy McFadden183ce582009-06-04 17:00:58 -0700587 markTouchedMethods(finalCurOffset);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800588
Andy McFadden01718122010-01-22 16:36:30 -0800589 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
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800602 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)
Andy McFaddenea414342010-08-25 12:05:44 -0700607 if (!gDvm.profilerWallClock) {
608 fprintf(state->traceFile, "clock=thread-cpu\n");
609 } else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800610#endif
Andy McFaddenea414342010-08-25 12:05:44 -0700611 {
612 fprintf(state->traceFile, "clock=wall\n");
613 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800614 fprintf(state->traceFile, "elapsed-time-usec=%llu\n", elapsed);
615 fprintf(state->traceFile, "num-method-calls=%d\n",
Andy McFadden183ce582009-06-04 17:00:58 -0700616 (finalCurOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800617 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
Andy McFadden01718122010-01-22 16:36:30 -0800633 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;
Elliott Hughesc5f53e22010-06-11 16:13:15 -0700650 LOGE("trace fwrite(%d) failed: %s\n",
651 finalCurOffset, strerror(err));
Andy McFadden01718122010-01-22 16:36:30 -0800652 dvmThrowExceptionFmt("Ljava/lang/RuntimeException;",
653 "Trace data write failed: %s", strerror(err));
654 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800655 }
656
Andy McFadden01718122010-01-22 16:36:30 -0800657 /* done! */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800658 free(state->buf);
659 state->buf = NULL;
660 fclose(state->traceFile);
661 state->traceFile = NULL;
662
Andy McFadden01718122010-01-22 16:36:30 -0800663 /* wake any threads that were waiting for profiling to complete */
Carl Shapirob31b3012010-05-25 18:35:37 -0700664 dvmBroadcastCond(&state->threadExitCond);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800665 dvmUnlockMutex(&state->startStopLock);
666}
667
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800668/*
669 * We just did something with a method. Emit a record.
670 *
671 * Multiple threads may be banging on this all at once. We use atomic ops
672 * rather than mutexes for speed.
673 */
674void dvmMethodTraceAdd(Thread* self, const Method* method, int action)
675{
676 MethodTraceState* state = &gDvm.methodTrace;
677 u4 clockDiff, methodVal;
678 int oldOffset, newOffset;
679 u1* ptr;
680
681 /*
682 * We can only access the per-thread CPU clock from within the
683 * thread, so we have to initialize the base time on the first use.
684 * (Looks like pthread_getcpuclockid(thread, &id) will do what we
685 * want, but it doesn't appear to be defined on the device.)
686 */
687 if (!self->cpuClockBaseSet) {
688 self->cpuClockBase = getClock();
689 self->cpuClockBaseSet = true;
690 //LOGI("thread base id=%d 0x%llx\n",
691 // self->threadId, self->cpuClockBase);
692 }
693
694 /*
695 * Advance "curOffset" atomically.
696 */
697 do {
698 oldOffset = state->curOffset;
699 newOffset = oldOffset + TRACE_REC_SIZE;
700 if (newOffset > state->bufferSize) {
701 state->overflow = true;
702 return;
703 }
Andy McFadden6e10b9a2010-06-14 15:24:39 -0700704 } while (android_atomic_release_cas(oldOffset, newOffset,
705 &state->curOffset) != 0);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800706
707 //assert(METHOD_ACTION((u4) method) == 0);
708
709 u8 now = getClock();
710 clockDiff = (u4) (now - self->cpuClockBase);
711
712 methodVal = METHOD_COMBINE((u4) method, action);
713
714 /*
715 * Write data into "oldOffset".
716 */
717 ptr = state->buf + oldOffset;
718 *ptr++ = self->threadId;
719 *ptr++ = (u1) methodVal;
720 *ptr++ = (u1) (methodVal >> 8);
721 *ptr++ = (u1) (methodVal >> 16);
722 *ptr++ = (u1) (methodVal >> 24);
723 *ptr++ = (u1) clockDiff;
724 *ptr++ = (u1) (clockDiff >> 8);
725 *ptr++ = (u1) (clockDiff >> 16);
726 *ptr++ = (u1) (clockDiff >> 24);
727}
728
Ben Cheng5cc61d72010-08-31 09:30:37 -0700729#if defined(WITH_INLINE_PROFILING)
730#include <interp/InterpDefs.h>
731
732/*
733 * Register the METHOD_TRACE_ENTER action for the fast interpreter and
734 * JIT'ed code.
735 */
736void dvmFastMethodTraceEnter(const Method* method,
737 const struct InterpState* interpState)
738{
739 if (gDvm.activeProfilers) {
740 dvmMethodTraceAdd(interpState->self, method, METHOD_TRACE_ENTER);
741 }
742}
743
744/*
745 * Register the METHOD_TRACE_EXIT action for the fast interpreter and
746 * JIT'ed code for Java methods. The about-to-return callee method can be
747 * retrieved from interpState->method.
748 */
749void dvmFastJavaMethodTraceExit(const struct InterpState* interpState)
750{
751 if (gDvm.activeProfilers) {
752 dvmMethodTraceAdd(interpState->self, interpState->method,
753 METHOD_TRACE_EXIT);
754 }
755}
756
757/*
758 * Register the METHOD_TRACE_EXIT action for the fast interpreter and
759 * JIT'ed code for JNI methods. The about-to-return JNI callee method is passed
760 * in explicitly.
761 */
762void dvmFastNativeMethodTraceExit(const Method* method,
763 const struct InterpState* interpState)
764{
765 if (gDvm.activeProfilers) {
766 dvmMethodTraceAdd(interpState->self, method, METHOD_TRACE_EXIT);
767 }
768}
769#endif
770
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800771/*
772 * We just did something with a method. Emit a record by setting a value
773 * in a magic memory location.
774 */
775void dvmEmitEmulatorTrace(const Method* method, int action)
776{
777#ifdef UPDATE_MAGIC_PAGE
778 /*
Jack Veenstra5b7abc52009-05-19 14:36:48 -0700779 * We store the address of the Dalvik bytecodes to the memory-mapped
780 * trace page for normal Java methods. We also trace calls to native
781 * functions by storing the address of the native function to the
782 * trace page.
783 * Abstract methods don't have any bytecodes, so we don't trace them.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800784 * (Abstract methods are never called, but in Dalvik they can be
Jack Veenstra5b7abc52009-05-19 14:36:48 -0700785 * because we do a "late trap" to a native method to generate the
786 * abstract method exception.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800787 */
Jack Veenstra5b7abc52009-05-19 14:36:48 -0700788 if (dvmIsAbstractMethod(method))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800789 return;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800790
Jack Veenstrac3b182a2009-05-05 11:29:51 -0700791 u4* pMagic = (u4*) gDvm.emulatorTracePage;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800792 u4 addr;
Jack Veenstra5b7abc52009-05-19 14:36:48 -0700793
794 if (dvmIsNativeMethod(method)) {
795 /*
796 * The "action" parameter is one of:
797 * 0 = ENTER
798 * 1 = EXIT
799 * 2 = UNROLL
800 * To help the trace tools reconstruct the runtime stack containing
801 * a mix of Java plus native methods, we add 4 to the action if this
802 * is a native method.
803 */
804 action += 4;
805
806 /*
807 * Get the address of the native function.
808 * This isn't the right address -- how do I get it?
809 * Fortunately, the trace tools can get by without the address, but
810 * it would be nice to fix this.
811 */
Andy McFadden686e1e22009-05-26 16:56:30 -0700812 addr = (u4) method->nativeFunc;
Jack Veenstra5b7abc52009-05-19 14:36:48 -0700813 } else {
814 /*
815 * The dexlist output shows the &DexCode.insns offset value, which
816 * is offset from the start of the base DEX header. Method.insns
817 * is the absolute address, effectively offset from the start of
818 * the optimized DEX header. We either need to return the
819 * optimized DEX base file address offset by the right amount, or
820 * take the "real" address and subtract off the size of the
821 * optimized DEX header.
822 *
823 * Would be nice to factor this out at dexlist time, but we can't count
824 * on having access to the correct optimized DEX file.
825 */
826 assert(method->insns != NULL);
827 const DexOptHeader* pOptHdr = method->clazz->pDvmDex->pDexFile->pOptHeader;
828 addr = (u4) method->insns - pOptHdr->dexOffset;
829 }
830
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800831 *(pMagic+action) = addr;
832 LOGVV("Set %p = 0x%08x (%s.%s)\n",
833 pMagic+action, addr, method->clazz->descriptor, method->name);
834#endif
835}
836
837/*
838 * The GC calls this when it's about to start. We add a marker to the
839 * trace output so the tool can exclude the GC cost from the results.
840 */
841void dvmMethodTraceGCBegin(void)
842{
843 TRACE_METHOD_ENTER(dvmThreadSelf(), gDvm.methodTrace.gcMethod);
844}
845void dvmMethodTraceGCEnd(void)
846{
847 TRACE_METHOD_EXIT(dvmThreadSelf(), gDvm.methodTrace.gcMethod);
848}
849
850/*
851 * The class loader calls this when it's loading or initializing a class.
852 */
853void dvmMethodTraceClassPrepBegin(void)
854{
855 TRACE_METHOD_ENTER(dvmThreadSelf(), gDvm.methodTrace.classPrepMethod);
856}
857void dvmMethodTraceClassPrepEnd(void)
858{
859 TRACE_METHOD_EXIT(dvmThreadSelf(), gDvm.methodTrace.classPrepMethod);
860}
861
862
863/*
864 * Enable emulator trace info.
865 */
866void dvmEmulatorTraceStart(void)
867{
Jack Veenstrac3b182a2009-05-05 11:29:51 -0700868 /* If we could not map the emulator trace page, then do not enable tracing */
869 if (gDvm.emulatorTracePage == NULL)
870 return;
871
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800872 updateActiveProfilers(1);
873
874 /* in theory we should make this an atomic inc; in practice not important */
875 gDvm.emulatorTraceEnableCount++;
876 if (gDvm.emulatorTraceEnableCount == 1)
877 LOGD("--- emulator method traces enabled\n");
878}
879
880/*
881 * Disable emulator trace info.
882 */
883void dvmEmulatorTraceStop(void)
884{
885 if (gDvm.emulatorTraceEnableCount == 0) {
886 LOGE("ERROR: emulator tracing not enabled\n");
Jack Veenstrac3b182a2009-05-05 11:29:51 -0700887 return;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800888 }
889 updateActiveProfilers(-1);
890 /* in theory we should make this an atomic inc; in practice not important */
891 gDvm.emulatorTraceEnableCount--;
892 if (gDvm.emulatorTraceEnableCount == 0)
893 LOGD("--- emulator method traces disabled\n");
894}
895
896
897/*
898 * Start instruction counting.
899 */
Andy McFadden5183a192010-10-22 15:26:07 -0700900void dvmStartInstructionCounting(void)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800901{
Ben Cheng5cc61d72010-08-31 09:30:37 -0700902#if defined(WITH_INLINE_PROFILING)
903 LOGW("Instruction counting not supported with inline profiling");
904#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800905 updateActiveProfilers(1);
906 /* in theory we should make this an atomic inc; in practice not important */
907 gDvm.instructionCountEnableCount++;
908}
909
910/*
Andy McFadden5183a192010-10-22 15:26:07 -0700911 * Stop instruction counting.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800912 */
Andy McFadden5183a192010-10-22 15:26:07 -0700913void dvmStopInstructionCounting(void)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800914{
915 if (gDvm.instructionCountEnableCount == 0) {
916 LOGE("ERROR: instruction counting not enabled\n");
917 dvmAbort();
918 }
919 updateActiveProfilers(-1);
920 gDvm.instructionCountEnableCount--;
921}
922
923
924/*
925 * Start alloc counting. Note this doesn't affect the "active profilers"
926 * count, since the interpreter loop is not involved.
927 */
928void dvmStartAllocCounting(void)
929{
930 gDvm.allocProf.enabled = true;
931}
932
933/*
934 * Stop alloc counting.
935 */
936void dvmStopAllocCounting(void)
937{
938 gDvm.allocProf.enabled = false;
939}