Merge change 8538
* changes:
A sampling profiler for Dalvik.
diff --git a/tools/dmtracedump/TraceDump.c b/tools/dmtracedump/TraceDump.c
index ae56d4d..2308148 100644
--- a/tools/dmtracedump/TraceDump.c
+++ b/tools/dmtracedump/TraceDump.c
@@ -14,6 +14,7 @@
** See the License for the specific language governing permissions and
** limitations under the License.
*/
+
/*
* Process dmtrace output.
*
@@ -127,6 +128,7 @@
typedef struct ThreadEntry {
int threadId;
const char* threadName;
+ uint64_t elapsedTime;
} ThreadEntry;
struct MethodEntry;
@@ -178,7 +180,7 @@
* The parsed contents of the key file.
*/
typedef struct DataKeys {
- char* fileData; /* contents of the entire file */
+ char* fileData; /* contents of the entire file */
long fileLen;
int numThreads;
ThreadEntry* threads;
@@ -398,6 +400,37 @@
/*
* This comparison function is called from qsort() to sort
+ * threads into decreasing order of elapsed time.
+ */
+int compareElapsed(const void *a, const void *b) {
+ const ThreadEntry *threadA, *threadB;
+ uint64_t elapsed1, elapsed2;
+ int result = 0;
+
+ threadA = (ThreadEntry const *)a;
+ threadB = (ThreadEntry const *)b;
+ elapsed1 = threadA->elapsedTime;
+ elapsed2 = threadB->elapsedTime;
+ if (elapsed1 < elapsed2)
+ return 1;
+ if (elapsed1 > elapsed2)
+ return -1;
+
+ /* If the elapsed times of two threads are equal, then sort them
+ * by thread id.
+ */
+ int idA = threadA->threadId;
+ int idB = threadB->threadId;
+ if (idA < idB)
+ result = -1;
+ if (idA > idB)
+ result = 1;
+
+ return result;
+}
+
+/*
+ * This comparison function is called from qsort() to sort
* TimedMethods into decreasing order of inclusive elapsed time.
*/
int compareTimedMethod(const void *a, const void *b) {
@@ -1078,7 +1111,7 @@
}
/*
- * Look up a method by it's method ID.
+ * Look up a method by its method ID (using binary search).
*
* Returns NULL if no matching method was found.
*/
@@ -1096,7 +1129,7 @@
id = pKeys->methods[mid].methodId;
if (id == methodId) /* match */
return &pKeys->methods[mid];
- else if (id < methodId) /* too low */
+ else if (id < methodId) /* too low */
lo = mid + 1;
else /* too high */
hi = mid - 1;
@@ -1490,6 +1523,7 @@
printf("<ul>\n");
printf(" <li><a href=\"#exclusive\">Exclusive profile</a></li>\n");
printf(" <li><a href=\"#inclusive\">Inclusive profile</a></li>\n");
+ printf(" <li><a href=\"#thread\">Thread profile</a></li>\n");
printf(" <li><a href=\"#class\">Class/method profile</a></li>\n");
printf(" <li><a href=\"#method\">Method/class profile</a></li>\n");
printf("</ul>\n\n");
@@ -1500,6 +1534,7 @@
printf("<a href=\"#contents\">[Top]</a>\n");
printf("<a href=\"#exclusive\">[Exclusive]</a>\n");
printf("<a href=\"#inclusive\">[Inclusive]</a>\n");
+ printf("<a href=\"#thread\">[Thread]</a>\n");
printf("<a href=\"#class\">[Class]</a>\n");
printf("<a href=\"#method\">[Method]</a>\n");
printf("<br><br>\n");
@@ -1765,6 +1800,65 @@
}
}
+void printThreadProfile(ThreadEntry *pThreads, int numThreads, uint64_t sumThreadTime)
+{
+ int ii;
+ ThreadEntry thread;
+ double total, per, sum_per;
+ uint64_t sum;
+ char threadBuf[HTML_BUFSIZE];
+ char anchor_buf[80];
+ char *anchor_close = "";
+
+ total = sumThreadTime;
+ anchor_buf[0] = 0;
+ if (gOptions.outputHtml) {
+ anchor_close = "</a>";
+ printf("<a name=\"thread\"></a>\n");
+ printf("<hr>\n");
+ outputNavigationBar();
+ } else {
+ printf("\n%s\n", profileSeparator);
+ }
+
+ /* Sort the threads into decreasing order of elapsed time. */
+ qsort(pThreads, numThreads, sizeof(ThreadEntry), compareElapsed);
+
+ printf("\nElapsed times for each thread, sorted by elapsed time.\n\n");
+
+ if (gOptions.outputHtml) {
+ printf("<br><br>\n<pre>\n");
+ }
+
+ printf(" Usecs self %% sum %% tid ThreadName\n");
+ sum = 0;
+
+ for (ii = 0; ii < numThreads; ++ii) {
+ int threadId;
+ char *threadName;
+ uint64_t time;
+
+ thread = pThreads[ii];
+
+ threadId = thread.threadId;
+ threadName = (char*)(thread.threadName);
+ time = thread.elapsedTime;
+
+ sum += time;
+ per = 100.0 * time / total;
+ sum_per = 100.0 * sum / total;
+
+ if (gOptions.outputHtml) {
+ threadName = htmlEscape(threadName, threadBuf, HTML_BUFSIZE);
+ }
+ printf("%9llu %6.2f %6.2f %3d %s\n", time, per, sum_per, threadId, threadName);
+ }
+
+ if (gOptions.outputHtml)
+ printf("</pre>\n");
+
+}
+
void createClassList(TraceData* traceData, MethodEntry **pMethods, int numMethods)
{
int ii;
@@ -2416,16 +2510,25 @@
*/
CallStack *pStack;
int threadId;
+ uint64_t elapsedTime = 0;
uint64_t sumThreadTime = 0;
for (threadId = 0; threadId < MAX_THREADS; ++threadId) {
+
pStack = traceData->stacks[threadId];
/* If this thread never existed, then continue with next thread */
if (pStack == NULL)
continue;
- /* Also, add up the time taken by all of the threads */
- sumThreadTime += pStack->lastEventTime - pStack->threadStartTime;
+ /* Calculate time spent in thread, and add it to total time */
+ elapsedTime = pStack->lastEventTime - pStack->threadStartTime;
+ sumThreadTime += elapsedTime;
+
+ /* Save the per-thread elapsed time in the DataKeys struct */
+ for (ii = 0; ii < dataKeys->numThreads; ++ii) {
+ if (dataKeys->threads[ii].threadId == threadId)
+ dataKeys->threads[ii].elapsedTime = elapsedTime;
+ }
for (ii = 0; ii < pStack->top; ++ii) {
if (ii == 0)
@@ -2479,7 +2582,8 @@
/*
* Produce a function profile from the following methods
*/
-void profileTrace(TraceData* traceData, MethodEntry **pMethods, int numMethods, uint64_t sumThreadTime)
+void profileTrace(TraceData* traceData, MethodEntry **pMethods, int numMethods, uint64_t sumThreadTime,
+ ThreadEntry *pThreads, int numThreads)
{
/* Print the html header, if necessary */
if (gOptions.outputHtml) {
@@ -2490,6 +2594,8 @@
printExclusiveProfile(pMethods, numMethods, sumThreadTime);
printInclusiveProfile(pMethods, numMethods, sumThreadTime);
+ printThreadProfile(pThreads, numThreads, sumThreadTime);
+
createClassList(traceData, pMethods, numMethods);
printClassProfiles(traceData, sumThreadTime);
@@ -2844,7 +2950,7 @@
if (gOptions.threshold < 0 || 100 <= gOptions.threshold) {
gOptions.threshold = 20;
}
-
+
if (gOptions.dump) {
dumpTrace();
return 0;
@@ -2870,7 +2976,8 @@
freeDataKeys(d2);
} else {
MethodEntry** methods = parseMethodEntries(dataKeys);
- profileTrace(&data1, methods, dataKeys->numMethods, sumThreadTime);
+ profileTrace(&data1, methods, dataKeys->numMethods, sumThreadTime,
+ dataKeys->threads, dataKeys->numThreads);
if (gOptions.graphFileName != NULL) {
createInclusiveProfileGraphNew(dataKeys);
}
diff --git a/vm/Common.h b/vm/Common.h
index 4b357e2..d0c021d 100644
--- a/vm/Common.h
+++ b/vm/Common.h
@@ -97,11 +97,15 @@
} JValue;
/*
- * Some systems might have this in <stdbool.h>.
+ * The <stdbool.h> definition uses _Bool, a type known to the compiler.
*/
-#ifndef __bool_true_false_are_defined
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h> /* C99 */
+#else
+# ifndef __bool_true_false_are_defined
typedef enum { false=0, true=!false } bool;
-#define __bool_true_false_are_defined 1
+# define __bool_true_false_are_defined 1
+# endif
#endif
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
diff --git a/vm/Globals.h b/vm/Globals.h
index 5b69f02..2ac1187 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -498,9 +498,12 @@
/*
* JDWP debugger support.
+ *
+ * Note "debuggerActive" is accessed from mterp, so its storage size and
+ * meaning must not be changed without updating the assembly sources.
*/
bool debuggerConnected; /* debugger or DDMS is connected */
- bool debuggerActive; /* debugger is making requests */
+ u1 debuggerActive; /* debugger is making requests */
JdwpState* jdwpState;
/*
diff --git a/vm/mterp/common/asm-constants.h b/vm/mterp/common/asm-constants.h
index 5c37af6..716df3d 100644
--- a/vm/mterp/common/asm-constants.h
+++ b/vm/mterp/common/asm-constants.h
@@ -82,7 +82,7 @@
*/
/* globals (sanity check for LDR vs LDRB) */
-MTERP_SIZEOF(sizeofGlobal_debuggerActive, gDvm.debuggerActive, MTERP_SMALL_ENUM)
+MTERP_SIZEOF(sizeofGlobal_debuggerActive, gDvm.debuggerActive, 1)
#if defined(WITH_PROFILER)
MTERP_SIZEOF(sizeofGlobal_activeProfilers, gDvm.activeProfilers, 4)
#endif