}
#endif
- //dvmLinearAllocDump(NULL);
-
-#if 0
- {
- extern int gDvm__totalInstr, gDvm__gcInstr, gDvm__gcData,
- gDvm__gcSimpleData;
- LOGI("GC DATA: totinst=%d, gcinst=%d, gcdata=%d simpled=%d\n",
- gDvm__totalInstr, gDvm__gcInstr, gDvm__gcData, gDvm__gcSimpleData);
- }
-#endif
-
free(bootClassPath);
LOGV("DexOpt command complete (result=%d)\n", result);
return result;
format "<code>21t</code>" is of length two, contains one register reference,
and additionally contains a branch target.</p>
-<p>Suggested static linking formats have an additional "<code>s</code>" suffix,
-making them four characters total. Similarly, suggested "inline" linking
-formats have an additional "<code>i</code>" suffix. (In this context, inline
-linking is like static linking, except with more direct ties into a
-virtual machine's implementation.) Finally, one oddball suggested format
-("<code>20bc</code>") includes two pieces of data which are represented in
-its format ID.</p>
+<p>Suggested static linking formats have an additional
+"<code>s</code>" suffix, making them four characters total. Similarly,
+suggested "inline" linking formats have an additional "<code>i</code>"
+suffix. (In this context, inline linking is like static linking,
+except with more direct ties into a virtual machine's implementation.)
+Finally, a couple oddball suggested formats (e.g.,
+"<code>20bc</code>") include two pieces of data which are both
+represented in its format ID.</p>
<p>The full list of typecode letters are as follows. Note that some
forms have different sizes, depending on the format:</p>
<td> </td>
</tr>
<tr>
+ <td><i>exop</i> BBBB<sub>lo</sub> BBBB<sub>hi</sub> AAAA</td></td>
+ <td>40sc</td>
+ <td><i><code>exop</code></i> AAAA, kind@BBBBBBBB</td>
+ <td><i>suggested format for statically determined verification errors;
+ see <code>20bc</code>, above</i></td>
+</tr>
+<tr>
<td><i>exop</i> BBBB<sub>lo</sub> BBBB<sub>hi</sub> AAAA
<td>41c</td>
<td><i><code>exop</code></i> vAAAA, field@BBBBBBBB<br/>
*/
private static final String[] JAVAX_CORE = {
"accessibility", "crypto", "imageio", "management", "naming", "net",
- "print", "rmi", "security", "sound", "sql", "swing", "transaction",
- "xml"
+ "print", "rmi", "security", "sip", "sound", "sql", "swing",
+ "transaction", "xml"
};
/** number of warnings during processing */
int firstReg = (regs.size() == 0) ? 0 : regs.get(0).getReg();
int count = regs.getWordCount();
- write(out, opcodeUnit(insn), cpi, (short) firstReg, (short) count);
+ write(out, opcodeUnit(insn), cpi, (short) count, (short) firstReg);
}
}
format 35ms
format 3rmi
format 3rms
+format 40sc
# One line per opcode. Columns are:
# hex for opcode
op 24ff invoke-direct/jumbo 5rc n method-ref continue|throw|invoke
op 25ff invoke-static/jumbo 5rc n method-ref continue|throw|invoke
op 26ff invoke-interface/jumbo 5rc n method-ref continue|throw|invoke
-# unused: op 27ff..ffff
+
+# unused: op 27ff..feff
+
+#
+# Optimized opcodes (not valid in an unoptimized dex file)
+#
+
+op ffff ^throw-verification-error/jumbo 40sc n varies optimized|throw
-/* //device/tools/dmtracedump/TraceDump.c
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
/*
* Process dmtrace output.
/* Size of temporary buffers for escaping html strings */
#define HTML_BUFSIZE 10240
-/* Size of methodId->method cache */
-#define METHOD_CACHE_SIZE 2048
-#define METHOD_CACHE_SIZE_MASK (METHOD_CACHE_SIZE - 1)
-
-/* Some filter constants */
-#define FILTER_TAG '*'
-#define FILTER_FLAG_THREAD '+'
-#define FILTER_TYPE_CLASS 0
-#define FILTER_TYPE_METHOD 1
-
-#define DEFAULT_ACTIVE_THREADS 8
-
char *htmlHeader =
"<html>\n<head>\n<script type=\"text/javascript\" src=\"%ssortable.js\"></script>\n"
"<script langugage=\"javascript\">\n"
typedef struct ThreadEntry {
int threadId;
const char* threadName;
- uint64_t elapsedTime;
} ThreadEntry;
struct MethodEntry;
ThreadEntry* threads;
int numMethods;
MethodEntry* methods; /* 2 extra methods: "toplevel" and "unknown" */
- int* methodCache; /* methodId->methodIndex mapping */
- // TODO change to map methodId->method itself
} DataKeys;
#define TOPLEVEL_INDEX 0
} StackEntry;
typedef struct CallStack {
- int top;
- StackEntry calls[MAX_STACK_DEPTH];
- uint64_t lastEventTime;
- uint64_t threadStartTime;
- uint64_t* remTimes;
- // Note: remTimes keeps a sum of 'un-allocated' time for each thread, in case
- // we need to allocate it to one (or many) filter later. This would happen when
- // we see a method exit that maches a filter, but whose entry we hadn't seen.
- // TODO: consider moving remTimes into FilterTimes and change logic appropriately
+ int top;
+ StackEntry calls[MAX_STACK_DEPTH];
+ uint64_t lastEventTime;
+ uint64_t threadStartTime;
} CallStack;
typedef struct DiffEntry {
const char* traceFileName;
const char* diffFileName;
const char* graphFileName;
- const char* filterFileName;
int keepDotFile;
int dump;
int outputHtml;
UniqueMethodEntry *uniqueMethods;
} TraceData;
-typedef struct FilterKey {
- int type[2]; /* 0=class, 1=method; 2 needed for start and end keys */
- uint32_t flags; /* 1st bit = include cross-thread time */
- char* keys[2]; /* 2 needed for start and end keys */
-} FilterKey;
-
-typedef struct FilterTimes {
- uint64_t totalWaitTime;
- uint64_t* threadWaitTimes;
- uint64_t* threadExecutionTimesWhileWaiting;
- uint64_t* threadExecutionTimes;
-} FilterTimes;
-
-typedef struct Filter {
- char* filterName;
- FilterKey* filterKeys;
- int numKeys;
- int activeCount;
- int* activeThreads;
- int* activationKeys;
- FilterTimes times;
-} Filter;
-
-int numFilters = 0; // global
-
static Options gOptions;
/* Escapes characters in the source string that are html special entities.
/*
* 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) {
free(pKeys->fileData);
free(pKeys->threads);
free(pKeys->methods);
- free(pKeys->methodCache);
free(pKeys);
}
return -1;
}
-int countLinesToChar(const char* data, int len, const char toFind)
+/*
+ * Count the number of lines until the next token.
+ *
+ * Returns -1 if none found before EOF.
+ */
+int countLinesToToken(const char* data, int len)
{
int count = 0;
int next;
- while (*data != toFind) {
+ while (*data != TOKEN_CHAR) {
next = findNextChar(data, len, '\n');
if (next < 0)
- return count;
+ return -1;
count++;
data += next+1;
len -= next+1;
}
/*
- * Count the number of lines until the next token.
- *
- * Returns 0 if none found before EOF.
- */
-int countLinesToToken(const char* data, int len)
-{
- return countLinesToChar(data, len, TOKEN_CHAR);
-}
-
-/*
* Make sure we're at the start of the right section.
*
* Returns the length of the token line, or -1 if something is wrong.
if (offset < 0)
goto fail;
+ /* Reduce our allocation now that we know where the end of the key section is. */
+ pKeys->fileData = (char *)realloc(pKeys->fileData, offset);
+ pKeys->fileLen = offset;
/* Leave fp pointing to the beginning of the data section. */
fseek(fp, offset, SEEK_SET);
printf("Methods (%d):\n", pKeys->numMethods);
for (i = 0; i < pKeys->numMethods; i++) {
printf("0x%08x %s : %s : %s\n",
- pKeys->methods[i].methodId >> 2, pKeys->methods[i].className,
+ pKeys->methods[i].methodId, pKeys->methods[i].className,
pKeys->methods[i].methodName, pKeys->methods[i].signature);
}
}
}
/*
- * Look up a method by its method ID (using binary search).
+ * Look up a method by it's method ID.
*
* Returns NULL if no matching method was found.
*/
{
int hi, lo, mid;
unsigned int id;
- int hashedId;
-
- /* Create cache if it doesn't already exist */
- if (pKeys->methodCache == NULL) {
- pKeys->methodCache = (int*) calloc(METHOD_CACHE_SIZE, sizeof(int));
- }
-
- // ids are multiples of 4, so shift
- hashedId = (methodId >> 2) & METHOD_CACHE_SIZE_MASK;
- if (pKeys->methodCache[hashedId]) /* cache hit */
- if (pKeys->methods[pKeys->methodCache[hashedId]].methodId == methodId)
- return &pKeys->methods[pKeys->methodCache[hashedId]];
lo = 0;
hi = pKeys->numMethods - 1;
mid = (hi + lo) / 2;
id = pKeys->methods[mid].methodId;
- if (id == methodId) { /* match, put in cache */
- hashedId = (methodId >> 2) & METHOD_CACHE_SIZE_MASK;
- pKeys->methodCache[hashedId] = mid;
- return &pKeys->methods[mid];
- } else if (id < methodId) /* too low */
+ if (id == methodId) /* match */
+ return &pKeys->methods[mid];
+ else if (id < methodId) /* too low */
lo = mid + 1;
else /* too high */
hi = mid - 1;
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");
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");
char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
char signatureBuf[HTML_BUFSIZE];
char anchor_buf[80];
+ char *anchor_close = "";
total = sumThreadTime;
anchor_buf[0] = 0;
if (gOptions.outputHtml) {
+ anchor_close = "</a>";
printf("<a name=\"inclusive\"></a>\n");
printf("<hr>\n");
outputNavigationBar();
}
}
-void printThreadProfile(ThreadEntry *pThreads, int numThreads, uint64_t sumThreadTime, Filter** filters)
-{
- int ii, jj;
- ThreadEntry thread;
- double total, per, sum_per;
- uint64_t sum;
- char threadBuf[HTML_BUFSIZE];
- char anchor_buf[80];
- int drawTable;
-
- total = sumThreadTime;
- anchor_buf[0] = 0;
- if (gOptions.outputHtml) {
- 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");
- printf("Also includes percentage of time spent during the <i>execution</i> of any filters.\n\n");
-
- if (gOptions.outputHtml) {
- printf("<br><br>\n<pre>\n");
- }
-
- printf(" Usecs self %% sum %%");
- for (ii = 0; ii < numFilters; ++ii) {
- printf(" %s %%", filters[ii]->filterName);
- }
- printf(" 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", time, per, sum_per);
- for (jj = 0; jj < numFilters; jj++) {
- printf(" %6.2f", 100.0 * filters[jj]->times.threadExecutionTimes[threadId] / time);
- }
- printf(" %3d %s\n", threadId, threadName);
- }
-
- if (gOptions.outputHtml)
- printf("</pre><br />");
-
- printf("\n\nBreak-down of portion of time spent by each thread while waiting on a filter method.\n");
-
- for (ii = 0; ii < numFilters; ++ii) {
- // Draw a table for each filter that measures wait time
- drawTable = 0;
- for (jj = 0; jj < filters[ii]->numKeys; jj++)
- if (filters[ii]->filterKeys[jj].flags == 1)
- drawTable = 1;
-
- if (drawTable) {
-
- if (gOptions.outputHtml)
- printf("<br/><br/>\n<pre>\n");
- printf("Filter: %s\n", filters[ii]->filterName);
- printf("Total waiting cycles: %llu (%6.2f%% of total)\n",
- filters[ii]->times.totalWaitTime,
- 100.0 * filters[ii]->times.totalWaitTime / sum);
-
- if (filters[ii]->times.totalWaitTime > 0) {
-
- printf("Details: \n\n");
-
- printf(" Waiting cycles %% of total waiting time execution time while waiting thread name\n");
-
- for (jj = 0; jj < numThreads; jj++) {
-
- thread = pThreads[jj];
-
- char *threadName;
- threadName = (char*) thread.threadName;
- if (gOptions.outputHtml) {
- threadName = htmlEscape(threadName, threadBuf, HTML_BUFSIZE);
- }
-
- printf(" %9llu %6.2f %6.2f %s\n",
- filters[ii]->times.threadWaitTimes[thread.threadId],
- 100.0 * filters[ii]->times.threadWaitTimes[thread.threadId] / filters[ii]->times.totalWaitTime,
- 100.0 * filters[ii]->times.threadExecutionTimesWhileWaiting[thread.threadId] / filters[ii]->times.totalWaitTime,
- threadName);
- }
- }
-
- if (gOptions.outputHtml)
- printf("</pre>\n");
-
- }
- }
-
-}
-
void createClassList(TraceData* traceData, MethodEntry **pMethods, int numMethods)
{
int ii;
}
/*
- * Determines whether the given FilterKey matches the method. The FilterKey's
- * key that is used to match against the method is determined by index.
- */
-int keyMatchesMethod(FilterKey filterKey, MethodEntry* method, int index)
-{
- if (filterKey.type[index] == 0) { // Class
-#if 0
- fprintf(stderr, " class is %s; filter key is %s\n", method->className, filterKey.keys[index]);
-#endif
- if (strcmp(method->className, filterKey.keys[index]) == 0) {
- return 1;
- }
- } else { // Method
- if (method->methodName != NULL) {
- // Get fully-qualified name
- // TODO: parse class name and method name an put them in structure to avoid
- // allocating memory here
- char* str = malloc ((strlen(method->className) + strlen(method->methodName) + 2) * sizeof(char));
- strcpy(str, method->className);
- strcat(str, ".");
- strcat(str, method->methodName);
-#if 0
- fprintf(stderr, " method is %s; filter key is %s\n", str, filterKey.keys[index]);
-#endif
- if (strcmp(str, filterKey.keys[index]) == 0) {
- free(str);
- return 1;
- }
- free(str);
- }
- }
- return 0;
-}
-
-/*
- * Adds the appropriate times to the given filter based on the given method. Activates and
- * de-activates filters as necessary.
- *
- * A filter is activated when the given method matches the 'entry' key of one of its FilterKeys.
- * It is de-activated when the method matches the 'exit' key of the same FilterKey that activated it
- * in the first place. Thus, a filter may be active more than once on the same thread (activated by
- * different FilterKeys). A filter may also be active on different threads at the same time.
- *
- * While the filter is active on thread 1, elapsed time is allocated to different buckets which
- * include: thread execution time (i.e., time thread 1 spent executing while filter was active),
- * thread waiting time (i.e., time thread 1 waited while other threads executed), and execution
- * time while waiting (i.e., time thread x spent executing while thread 1 was waiting). We also
- * keep track of the total waiting time for a given filter.
- *
- * Lastly, we keep track of remaining (un-allocated) time for cases in which we exit a method we
- * had not entered before, and that method happens to match the 'exit' key of a FilterKey.
- */
-int filterMethod(MethodEntry* method, Filter* filter, int entry, int threadId, int numThreads,
- uint64_t elapsed, uint64_t remTime)
-{
- int ii, jj;
- int activeCount, addedWaitTimeThreadsCount;
- int* activeThreads;
- int* activationKeys;
- int* addedWaitTimeThreads;
-
- // flags
- int addWaitTime = 0;
- int deactivation = 0;
- int addedExecutionTime = 0;
- int addedExecutionTimeWhileWaiting = 0;
- int addedWaitTime;
- int addedRemTime = 0;
- int threadKeyPairActive = 0;
-
- if (filter->times.threadWaitTimes == NULL && filter->times.threadExecutionTimes == NULL &&
- filter->times.threadExecutionTimesWhileWaiting == NULL) {
- filter->times.threadWaitTimes = (uint64_t*) calloc(MAX_THREADS, sizeof(uint64_t));
- filter->times.threadExecutionTimesWhileWaiting =
- (uint64_t*) calloc(MAX_THREADS, sizeof(uint64_t));
- filter->times.threadExecutionTimes = (uint64_t*) calloc(MAX_THREADS, sizeof(uint64_t));
- }
-
- int verbose = 0;
-
- if (verbose)
- fprintf(stderr,
- "Running %s filter for class %s method %s, thread %d; activeCount: %d time: %llu\n",
- filter->filterName, method->className, method->methodName, threadId,
- filter->activeCount, elapsed);
-
- // If active on some thread
- if (filter->activeCount > 0) {
-
- // Initialize active structures in case there are any de-activations
- activeThreads = (int*) calloc(filter->activeCount, sizeof(int));
- activationKeys = (int*) calloc(filter->activeCount, sizeof(int));
- activeCount = 0;
-
- // Initialize structure to help us determine which threads we've already added wait time to
- addedWaitTimeThreads = (int*) calloc(filter->activeCount, sizeof(int));
- addedWaitTimeThreadsCount = 0;
-
- // Add times to appropriate sums and de-activate (if necessary)
- for (ii = 0; ii < filter->activeCount; ii++) {
-
- if (verbose) {
- fprintf(stderr, " Analyzing active thread with id %d, activated by key [%s, %s]\n",
- filter->activeThreads[ii],
- filter->filterKeys[filter->activationKeys[ii]].keys[0],
- filter->filterKeys[filter->activationKeys[ii]].keys[1]);
- }
-
- // If active on THIS thread -> add to execution time (only add once!)
- if (filter->activeThreads[ii] == threadId && !addedExecutionTime) {
- if (verbose)
- fprintf(stderr, " Adding execution time to this thead\n");
- filter->times.threadExecutionTimes[threadId] += elapsed;
- addedExecutionTime = 1;
- }
-
- // If active on ANOTHER thread (or this one too) with CROSS_THREAD_FLAG -> add to
- // both thread's waiting time + total
- if (filter->filterKeys[filter->activationKeys[ii]].flags == 1) {
-
- // Add time to thread that is waiting (add to each waiting thread at most once!)
- addedWaitTime = 0;
- for (jj = 0; jj < addedWaitTimeThreadsCount; jj++) {
- if (addedWaitTimeThreads[jj] == filter->activeThreads[ii])
- addedWaitTime = 1;
- }
- if (!addedWaitTime) {
- if (verbose)
- fprintf(stderr, " Adding wait time to waiting thread\n");
- filter->times.threadWaitTimes[filter->activeThreads[ii]] += elapsed;
- addedWaitTimeThreads[addedWaitTimeThreadsCount++] = filter->activeThreads[ii];
- }
-
- // Add execution time to this thread while the other is waiting (only add once!)
- // [Flag is needed only because outside for loop might iterate through same
- // thread twice?] TODO: verify
- if (!addedExecutionTimeWhileWaiting) {
- if (verbose)
- fprintf(stderr, " Adding exec time to this thread while thread waits\n");
- filter->times.threadExecutionTimesWhileWaiting[threadId] += elapsed;
- addedExecutionTimeWhileWaiting = 1;
- }
-
- addWaitTime = 1;
- }
-
- // If a method exit matches the EXIT method of an ACTIVE key -> de-activate
- // the KEY (not the entire filter!!)
- if (!entry && keyMatchesMethod(filter->filterKeys[filter->activationKeys[ii]],
- method, 1)) {
- if (verbose)
- fprintf(stderr, " Exit key matched!\n");
-
- // Deactivate by removing (NOT adding) entries from activeThreads and activationKeys
- deactivation = 1; // singal that lists should be replaced
- } else {
- // No de-activation -> copy old entries into new lists
- activeThreads[activeCount] = filter->activeThreads[ii];
- activationKeys[activeCount++] = filter->activationKeys[ii];
- }
- }
-
- // If waiting on ANY thread, add wait time to total (but only ONCE!)
- if (addWaitTime) {
- filter->times.totalWaitTime += elapsed;
- }
-
- // If de-activation occurred, replace lists
- if (deactivation) {
- // TODO: Free memory from old lists
-
- // Set new lists
- filter->activeThreads = activeThreads;
- filter->activationKeys = activationKeys;
- filter->activeCount = activeCount;
- } else {
- // TODO: Free memory from new lists
- }
-
- } // Else, continue (we might be activating the filter on a different thread)
-
-
- if (entry) { // ENTRY
- if (verbose)
- fprintf(stderr, " Here at the entry\n");
- // If method matches entry key -> activate thread (do not add time since it's a new entry!)
- for (ii = 0; ii < filter->numKeys; ii++) {
- if (keyMatchesMethod(filter->filterKeys[ii], method, 0)) {
- if (verbose)
- fprintf(stderr, " Entry key matched!\n");
- // Activate thread only if thread/key pair is not already active
- for (jj = 0; jj < filter->activeCount; jj++) {
- if (filter->activeThreads[jj] == threadId && filter->activationKeys[jj] == ii)
- threadKeyPairActive = 1;
- }
- // TODO: WORRY ABOUT MEMORY WHEN ACTIVE_COUNT > DEFAULT_ACTIVE_THREAD (unlikely)
- // TODO: what if the same thread is active multiple times by different keys?
- // nothing, we just have to make sure we dont double-add, and we dont..
- if (!threadKeyPairActive) {
- filter->activeThreads[filter->activeCount] = threadId;
- filter->activationKeys[filter->activeCount++] = ii;
- }
- }
- }
- } else { // EXIT
- // If method matches a terminal key -> add remTime to total (no need to active/de-activate)
- for (ii = 0; ii < filter->numKeys; ii++) {
- if (!deactivation && keyMatchesMethod(filter->filterKeys[ii], method, 1) &&
- keyMatchesMethod(filter->filterKeys[ii], method, 0)) {
- // Add remTime(s)
- // TODO: think about how we should add remTimes.. should we add remTime to threads
- // that were waiting or being waited on? for now, keep it simple and just add the
- // execution time to the current thread.
- filter->times.threadExecutionTimes[threadId] += remTime;
- addedRemTime = 1;
- }
- }
- }
-
- return addedExecutionTime | (addedRemTime << 1);
-}
-
-void dumpFilters(Filter** filters) {
- int i;
- for (i = 0; i < numFilters; i++) {
- int j;
- fprintf(stderr, "FILTER %s\n", filters[i]->filterName);
- for (j = 0; j < filters[i]->numKeys; j++) {
- fprintf(stderr, "Keys: %s, type %d", filters[i]->filterKeys[j].keys[0],
- filters[i]->filterKeys[j].type[0]);
- if (filters[i]->filterKeys[j].keys[1] != NULL) {
- fprintf(stderr, " AND %s, type %d", filters[i]->filterKeys[j].keys[1],
- filters[i]->filterKeys[j].type[1]);
- }
- fprintf(stderr, "; flags: %d\n", filters[i]->filterKeys[j].flags);
- }
- }
-}
-
-/*
- * See parseFilters for required data format.
- * 'data' must point to the beginning of a filter definition.
- */
-char* parseFilter(char* data, char* dataEnd, Filter** filters, int num) {
-
- Filter* filter;
- int next, count, i;
- int tmpOffset, tmpKeyLen;
- char* tmpKey;
- char* key1;
- char* key2;
-
- filter = (Filter*) malloc(sizeof(Filter));
- filter->activeCount = 0;
- filter->activeThreads = (int*) calloc(DEFAULT_ACTIVE_THREADS, sizeof(int));
- filter->activationKeys = (int*) calloc(DEFAULT_ACTIVE_THREADS, sizeof(int));
-
- next = findNextChar(data + 1, dataEnd - data - 1, '\n');
- if (next < 0) {
- // TODO: what should we do here?
- // End of file reached...
- }
- data[next+1] = '\0';
- filter->filterName = data + 1;
- data += next + 2; // Careful
-
- /*
- * Count the number of keys (one per line).
- */
- count = countLinesToChar(data, dataEnd - data, FILTER_TAG);
- if (count <= 0) {
- fprintf(stderr,
- "ERROR: failed while parsing filter %s (found %d keys)\n",
- filter->filterName, count);
- return NULL; // TODO: Should do something else
- // Could return filter with 0 keys instead (probably better to avoid random segfaults)
- }
-
- filter->filterKeys = (FilterKey*) malloc(sizeof(FilterKey) * count);
-
- /*
- * Extract all entries.
- */
- tmpOffset = 0;
- for (i = 0; i < count; i++) {
- next = findNextChar(data, dataEnd - data, '\n');
- // assert(next > 0); // TODO: revise... (skip if next == 0 ?)
- data[next] = '\0';
- tmpKey = data;
-
- if (*data == FILTER_FLAG_THREAD) {
- filter->filterKeys[i].flags = 1;
- tmpKey++;
- } else {
- filter->filterKeys[i].flags = 0;
- }
-
- tmpOffset = findNextChar(tmpKey, next, ',');
-
- if (tmpOffset < 0) {
- // No comma, so only 1 key
- key1 = tmpKey;
- key2 = tmpKey;
-
- // Get type for key1
- filter->filterKeys[i].type[0] = FILTER_TYPE_CLASS; // default
- tmpOffset = findNextChar(key1, next, '(');
- if (tmpOffset > 0) {
- if (findNextChar(key1, next, ')') == tmpOffset + 1) {
- filter->filterKeys[i].type[0] = FILTER_TYPE_METHOD;
- filter->filterKeys[i].type[1] = FILTER_TYPE_METHOD;
- }
- key1[tmpOffset] = '\0';
- }
- } else {
- // Pair of keys
- tmpKey[tmpOffset] = '\0';
- key1 = tmpKey;
- key2 = tmpKey + tmpOffset + 1;
-
- // Get type for key1
- filter->filterKeys[i].type[0] = FILTER_TYPE_CLASS;
- tmpKeyLen = tmpOffset;
- tmpOffset = findNextChar(key1, tmpKeyLen, '(');
- if (tmpOffset > 0) {
- if (findNextChar(key1, tmpKeyLen, ')') == tmpOffset + 1) {
- filter->filterKeys[i].type[0] = FILTER_TYPE_METHOD;
- }
- key1[tmpOffset] = '\0';
- }
-
- // Get type for key2
- filter->filterKeys[i].type[1] = FILTER_TYPE_CLASS;
- tmpOffset = findNextChar(key2, next - tmpKeyLen, '(');
- if (tmpOffset > 0) {
- if (findNextChar(key2, next - tmpKeyLen, ')') == tmpOffset + 1) {
- filter->filterKeys[i].type[1] = FILTER_TYPE_METHOD;
- }
- key2[tmpOffset] = '\0';
- }
- }
-
- filter->filterKeys[i].keys[0] = key1;
- filter->filterKeys[i].keys[1] = key2;
- data += next+1;
- }
-
- filter->numKeys = count;
- filters[num] = filter;
-
- return data;
-}
-
-/*
- * Parses filters from given file. The file must follow the following format:
- *
- * *FilterName <- creates a new filter with keys to follow
- * A.method() <- key that triggers whenever A.method() enters/exit
- * Class <- key that triggers whenever any method from Class enters/exits
- * +CrossThread <- same as above, but keeps track of execution times accross threads
- * B.m(),C.m() <- key that triggers filter on when B.m() enters and off when C.m() exits
- *
- * TODO: add concrete example to make things clear
- */
-Filter** parseFilters(const char* filterFileName) {
-
- Filter** filters = NULL;
- FILE* fp = NULL;
- long len;
- char* data;
- char* dataEnd;
- char* dataStart;
- int i, next, count;
-
- fp = fopen(filterFileName, "r");
- if (fp == NULL)
- goto bail;
-
- if (fseek(fp, 0L, SEEK_END) != 0) {
- perror("fseek");
- goto bail;
- }
-
- len = ftell(fp);
- if (len == 0) {
- fprintf(stderr, "WARNING: Filter file is empty.\n");
- goto bail;
- }
- rewind(fp);
-
- data = (char*) malloc(len);
- if (data == NULL) {
- fprintf(stderr, "ERROR: unable to alloc %ld bytes for filter file\n", len);
- goto bail;
- }
-
- // Read file into memory
- if (fread(data, 1, len, fp) != (size_t) len) {
- fprintf(stderr, "ERROR: unable to read %ld bytes from filter file\n", len);
- goto bail;
- }
-
- dataStart = data;
- dataEnd = data + len;
-
- // Figure out how many filters there are
- numFilters = 0;
- next = -1;
-
- while (1) {
- if (*data == FILTER_TAG)
- numFilters++;
- next = findNextChar(data, len, '\n');
- if (next < 0)
- break;
- data += next+1;
- len -= next+1;
- }
-
- if (numFilters == 0) {
- fprintf(stderr, "WARNING: no filters found. Continuing without filters\n");
- goto bail;
- }
-
- filters = (Filter**) calloc(numFilters, sizeof(Filter *));
- if (filters == NULL) {
- fprintf(stderr, "ERROR: unable to alloc memory for filters");
- goto bail;
- }
-
- data = dataStart;
- for (i = 0; i < numFilters; i++) {
- data = parseFilter(data, dataEnd, filters, i);
- }
-
- return filters;
-
-bail:
- if (fp != NULL)
- fclose(fp);
-
- return NULL;
-
-}
-
-
-/*
* Read the key and data files and return the MethodEntries for those files
*/
-DataKeys* parseDataKeys(TraceData* traceData, const char* traceFileName,
- uint64_t* threadTime, Filter** filters)
+DataKeys* parseDataKeys(TraceData* traceData, const char* traceFileName, uint64_t* threadTime)
{
DataKeys* dataKeys = NULL;
MethodEntry **pMethods = NULL;
MethodEntry* method;
FILE* dataFp = NULL;
DataHeader dataHeader;
- int ii, jj, numThreads;
+ int ii;
uint64_t currentTime;
MethodEntry* caller;
goto bail;
if ((dataKeys = parseKeys(dataFp, 0)) == NULL)
- goto bail;
+ goto bail;
if (parseDataHeader(dataFp, &dataHeader) < 0)
goto bail;
- numThreads = dataKeys->numThreads;
-
#if 0
FILE *dumpStream = fopen("debug", "w");
#endif
int action;
unsigned int methodId;
CallStack *pStack;
-
/*
* Extract values from file.
*/
pStack->top = 0;
pStack->lastEventTime = currentTime;
pStack->threadStartTime = currentTime;
- pStack->remTimes = (uint64_t*) calloc(numFilters, sizeof(uint64_t));
traceData->stacks[threadId] = pStack;
}
#if 0
if (method->methodName) {
- fprintf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s.%s %s\n",
- threadId, currentTime, action, pStack->threadStartTime,
- method->recursiveEntries,
- pStack->top, method->className, method->methodName,
- method->signature);
+ fprintf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s.%s %s\n",
+ threadId, currentTime, action, pStack->threadStartTime,
+ method->recursiveEntries,
+ pStack->top, method->className, method->methodName,
+ method->signature);
} else {
- printf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s\n",
- threadId, currentTime, action, pStack->threadStartTime,
- method->recursiveEntries,
- pStack->top, method->className);
+ fprintf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s\n",
+ threadId, currentTime, action, pStack->threadStartTime,
+ method->recursiveEntries,
+ pStack->top, method->className);
}
#endif
/* Push the method on the stack for this thread */
pStack->calls[pStack->top].method = method;
pStack->calls[pStack->top++].entryTime = currentTime;
-
- // For each filter
- int result = 0;
- for (ii = 0; ii < numFilters; ii++) {
- result = filterMethod(method, filters[ii], 1, threadId, numThreads,
- currentTime - pStack->lastEventTime, pStack->remTimes[ii]);
-
- // TODO: make remTimes work properly
- // Consider moving remTimes handling together with the rest
- // of time handling and clean up the return codes
- /*
- if (result == 0) { // no time added, no remTime added
- pStack->remTimes[ii] += currentTime - pStack->lastEventTime;
- } else if (result == 3 || result == 4) { // remTime added
- // Reset remTime, since it's been added
- pStack->remTimes[ii] = 0;
- }
- */
- }
-
} else {
/* This is a method exit */
uint64_t entryTime = 0;
if (method->recursiveEntries == 0) {
method->topExclusive += currentTime - pStack->lastEventTime;
}
-
- // For each filter
- int result = 0;
- for (ii = 0; ii < numFilters; ii++) {
- result = filterMethod(method, filters[ii], 0, threadId, numThreads,
- currentTime - pStack->lastEventTime, pStack->remTimes[ii]);
-
- // TODO: make remTimes work properly
- /*
- if (result == 0) { // no time added, no remTime added
- pStack->remTimes[ii] += currentTime - pStack->lastEventTime;
- } else if (result == 3 || result == 4) { // remTime added
- // Reset remTime, since it's been added
- pStack->remTimes[ii] = 0;
- }
- */
- }
-
}
/* Remember the time of the last entry or exit event */
pStack->lastEventTime = currentTime;
*/
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;
- /* Calculate times spent in thread, and add it to total time */
- elapsedTime = pStack->lastEventTime - pStack->threadStartTime;
- sumThreadTime += elapsedTime;
+ /* Also, add up the time taken by all of the threads */
+ sumThreadTime += pStack->lastEventTime - pStack->threadStartTime;
for (ii = 0; ii < pStack->top; ++ii) {
- //printf("in loop\n");
-
if (ii == 0)
caller = &dataKeys->methods[TOPLEVEL_INDEX];
else
uint64_t entryTime = pStack->calls[ii].entryTime;
uint64_t elapsed = pStack->lastEventTime - entryTime;
addInclusiveTime(caller, method, elapsed);
-
- // For each filter
- int result = 0;
- for (ii = 0; ii < numFilters; ii++) {
- result = filterMethod(method, filters[ii], 0, threadId, numThreads,
- currentTime - pStack->lastEventTime, pStack->remTimes[ii]);
-
- // TODO: make remTimes work properly
- /*
- if (result == 0) { // no time added, no remTime added
- pStack->remTimes[ii] += currentTime - pStack->lastEventTime;
- } else if (result == 3 || result == 4) { // remTime added
- // Reset remTime, since it's been added
- pStack->remTimes[ii] = 0;
- }
- */
- }
}
-
- /* 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;
- }
- }
-
-
}
caller = &dataKeys->methods[TOPLEVEL_INDEX];
caller->elapsedInclusive = sumThreadTime;
return pMethods;
}
-
/*
* Produce a function profile from the following methods
*/
-void profileTrace(TraceData* traceData, MethodEntry **pMethods, int numMethods, uint64_t sumThreadTime,
- ThreadEntry *pThreads, int numThreads, Filter** filters)
+void profileTrace(TraceData* traceData, MethodEntry **pMethods, int numMethods, uint64_t sumThreadTime)
{
- /* Print the html header, if necessary */
+ /* Print the html header, if necessary */
if (gOptions.outputHtml) {
printf(htmlHeader, gOptions.sortableUrl);
outputTableOfContents();
printExclusiveProfile(pMethods, numMethods, sumThreadTime);
printInclusiveProfile(pMethods, numMethods, sumThreadTime);
- printThreadProfile(pThreads, numThreads, sumThreadTime, filters);
-
createClassList(traceData, pMethods, numMethods);
printClassProfiles(traceData, sumThreadTime);
if (gOptions.outputHtml) {
printf("</table>\n");
printf("<h3>Run 1 methods not found in Run 2</h3>");
- printf(tableHeaderMissing);
+ printf(tableHeaderMissing, "?");
}
for (i = 0; i < d1->numMethods; ++i) {
if (gOptions.outputHtml) {
printf("</table>\n");
printf("<h3>Run 2 methods not found in Run 1</h3>");
- printf(tableHeaderMissing);
+ printf(tableHeaderMissing, "?");
}
for (i = 0; i < d2->numMethods; ++i) {
int usage(const char *program)
{
- fprintf(stderr, "usage: %s [-ho] [-s sortable] [-d trace-file-name] [-g outfile] [-f filter-file] trace-file-name\n", program);
+ fprintf(stderr, "Copyright (C) 2006 The Android Open Source Project\n\n");
+ fprintf(stderr, "usage: %s [-ho] [-s sortable] [-d trace-file-name] [-g outfile] trace-file-name\n", program);
fprintf(stderr, " -d trace-file-name - Diff with this trace\n");
fprintf(stderr, " -g outfile - Write graph to 'outfile'\n");
- fprintf(stderr, " -f filter-file - Filter functions as specified in file\n");
fprintf(stderr, " -k - When writing a graph, keep the intermediate DOT file\n");
fprintf(stderr, " -h - Turn on HTML output\n");
fprintf(stderr, " -o - Dump the dmtrace file instead of profiling\n");
int parseOptions(int argc, char **argv)
{
while (1) {
- int opt = getopt(argc, argv, "d:hg:kos:t:f:");
+ int opt = getopt(argc, argv, "d:hg:kos:t:");
if (opt == -1)
break;
switch (opt) {
case 'g':
gOptions.graphFileName = optarg;
break;
- case 'f':
- gOptions.filterFileName = optarg;
- break;
case 'k':
gOptions.keepDotFile = 1;
break;
*/
int main(int argc, char** argv)
{
-
gOptions.threshold = -1;
// Parse the options
uint64_t sumThreadTime = 0;
- Filter** filters = NULL;
- if (gOptions.filterFileName != NULL) {
- filters = parseFilters(gOptions.filterFileName);
- }
-
TraceData data1;
- memset(&data1, 0, sizeof(data1));
DataKeys* dataKeys = parseDataKeys(&data1, gOptions.traceFileName,
- &sumThreadTime, filters);
+ &sumThreadTime);
if (dataKeys == NULL) {
fprintf(stderr, "Cannot read trace.\n");
exit(1);
if (gOptions.diffFileName != NULL) {
uint64_t sum2;
TraceData data2;
- DataKeys* d2 = parseDataKeys(&data2, gOptions.diffFileName, &sum2, filters);
+ DataKeys* d2 = parseDataKeys(&data2, gOptions.diffFileName, &sum2);
createDiff(d2, sum2, dataKeys, sumThreadTime);
freeDataKeys(d2);
} else {
MethodEntry** methods = parseMethodEntries(dataKeys);
- profileTrace(&data1, methods, dataKeys->numMethods, sumThreadTime,
- dataKeys->threads, dataKeys->numThreads, filters);
+ profileTrace(&data1, methods, dataKeys->numMethods, sumThreadTime);
if (gOptions.graphFileName != NULL) {
createInclusiveProfileGraphNew(dataKeys);
}
+++ /dev/null
-*GC
-dvmGcScanRootClassLoader
-mspace_walk_free_pages
-dvmCollectGarbageInternal
-doHeapWork
-dvmGetNextHeapWorkerObject
-GC
-GC2
-GC3
-*Net
-setsockopt
-+sys_setsockopt [kernel]
-socketSelect
-send
-recv
-sendto
-recvfrom
-+sys_sendto [kernel]
-+sys_recvfrom [kernel]
-org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnection
-android.net.http.ConnectionThread
-PlainSocketImpl
-WebCore::HTMLTokenizer
-*IO
-select
-+sys_select [kernel]
-*DB
-android.database.sqlite.SQLiteOpenHelper
-android.database.sqlite.SQLiteQueryBuilder
-android.database.sqlite.SQLiteDatabase
-android.database.sqlite.SQLiteDirectCursorDriver
-android.database.sqlite.SQLiteQuery
-android.database.sqlite.SQLiteProgram
-android.database.AbstractCursor
-android.database.sqlite.SQLiteCursor
-*UI
-android.view.View.draw()
-android.view.ViewGroup
-*Sync
-+java.lang.Object.wait()
-*Useless
-+android.widget.ProgressBar
+++ /dev/null
-#!/bin/bash
-
-failed=0
-for file in $(find $1 -type f -iname 'test*'); do
- case $file in
- *testFilters) continue; ;;
- *Expected) continue; ;;
- *Trace) continue; ;;
- *.html) continue; ;;
- esac
-
- echo "Running test for $file"
-
-# create_test_dmtrace $file tmp.trace
- dmtracedump -f testFilters -h "$file"Trace > tmp.html 2> /dev/null
-
- output=`diff tmp.html "$file"Expected 2>&1`
- if [ ${#output} -eq 0 ]
- then
- echo " OK"
- else
- echo " Test failed: $output"
- failed=`expr $failed + 1`
- fi
-
-done
-
-rm tmp.trace
-rm tmp.html
-
-if [ $failed -gt 0 ]
-then
- echo "$failed test(s) failed"
-else
- echo "All tests passed successfully"
-fi
+++ /dev/null
-*FirstFilter
-+A.m(),B.m()
-+C.m()
-+R.m(),S.m()
-*SecondFilter
-+D.m(),E.m()
-+F.m()
-*RepeatedFilter
-+R.m(),S.m()
+++ /dev/null
-# ____ ____ _________
-# __|A |___________|B |_____|Z |_______
-#
-# ___________ ____ ____
-# _______|Z |_____|D |_________|E |__
-#
-#
-0 1 A
-2 1 A
-0 2 Z
-4 2 Z
-2 1 B
-4 1 B
-4 2 D
-6 2 D
-4 1 Z
-8 1 Z
-6 2 E
-8 2 E
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 16
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 2 12.50 62.50 <a href="#m2">[2]</a> A.m ()
- 2 12.50 75.00 <a href="#m3">[3]</a> B.m ()
- 2 12.50 87.50 <a href="#m4">[4]</a> D.m ()
- 2 12.50 100.00 <a href="#m5">[5]</a> E.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 16 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 2/2 8 Z.m ()
- 12.5% <a href="#m2">[2]</a> 1/1 2 A.m ()
- 12.5% <a href="#m3">[3]</a> 1/1 2 B.m ()
- 12.5% <a href="#m4">[4]</a> 1/1 2 D.m ()
- 12.5% <a href="#m5">[5]</a> 1/1 2 E.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 8 (toplevel)
-[1] 50.0% 2+0 8 Z.m ()
- 100.0% excl 8
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[2] 12.5% 1+0 2 A.m ()
- 100.0% excl 2
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 12.5% 1+0 2 B.m ()
- 100.0% excl 2
-<a name="m4"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[4] 12.5% 1+0 2 D.m ()
- 100.0% excl 2
-<a name="m5"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[5] 12.5% 1+0 2 E.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 8 50.00 50.00 50.00 0.00 0.00 1 main
- 8 50.00 100.00 0.00 50.00 0.00 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 8 ( 50.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 100.00 50.00 main
- 0 0.00 50.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 8 ( 50.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 0 0.00 50.00 main
- 8 100.00 50.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 8 50.0 50.0 2+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 8 8 100.0 100.0 2+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 2 12.5 62.5 1+0 A</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 12.5 75.0 1+0 B</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span> 2 12.5 87.5 1+0 D</div>
-<div class="parent" id="d3">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m4">[4]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span> 2 12.5 100.0 1+0 E</div>
-<div class="parent" id="d4">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m5">[5]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 16 100.0 100.0 6+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 8 8 50.0 50.0 2+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 62.5 1+0 <a href="#m2">[2]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 75.0 1+0 <a href="#m3">[3]</a> B.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 87.5 1+0 <a href="#m4">[4]</a> D.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 100.0 1+0 <a href="#m5">[5]</a> E.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ _________
-# __|R |___________|S |_____|Z |_______
-#
-# ___________ ____ ____
-# _______|Z |_____|R |_________|S |__
-#
-#
-0 1 R
-2 1 R
-0 2 Z
-4 2 Z
-2 1 S
-4 1 S
-4 2 R
-6 2 R
-4 1 Z
-8 1 Z
-6 2 S
-8 2 S
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 16
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 4 25.00 75.00 <a href="#m2">[2]</a> R.m ()
- 4 25.00 100.00 <a href="#m3">[3]</a> S.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 16 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 2/2 8 Z.m ()
- 25.0% <a href="#m2">[2]</a> 2/2 4 R.m ()
- 25.0% <a href="#m3">[3]</a> 2/2 4 S.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 8 (toplevel)
-[1] 50.0% 2+0 8 Z.m ()
- 100.0% excl 8
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[2] 25.0% 2+0 4 R.m ()
- 100.0% excl 4
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[3] 25.0% 2+0 4 S.m ()
- 100.0% excl 4
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 8 50.00 50.00 50.00 0.00 50.00 1 main
- 8 50.00 100.00 50.00 0.00 50.00 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 16 (100.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 50.00 50.00 main
- 8 50.00 50.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 16 (100.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 50.00 50.00 main
- 8 50.00 50.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 8 50.0 50.0 2+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 8 8 100.0 100.0 2+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 4 25.0 75.0 2+0 R</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 4 25.0 100.0 2+0 S</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 16 100.0 100.0 6+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 8 8 50.0 50.0 2+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 25.0 75.0 2+0 <a href="#m2">[2]</a> R.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 25.0 100.0 2+0 <a href="#m3">[3]</a> S.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ _________
-# __|A |___________|B |_____|Z |_______
-#
-# ___________ ____ ____
-# _______|Z |_____|R |_________|S |__
-#
-#
-0 1 A
-2 1 A
-0 2 Z
-4 2 Z
-2 1 B
-4 1 B
-4 2 R
-6 2 R
-4 1 Z
-8 1 Z
-6 2 S
-8 2 S
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 16
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 2 12.50 62.50 <a href="#m2">[2]</a> A.m ()
- 2 12.50 75.00 <a href="#m3">[3]</a> B.m ()
- 2 12.50 87.50 <a href="#m4">[4]</a> R.m ()
- 2 12.50 100.00 <a href="#m5">[5]</a> S.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 16 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 2/2 8 Z.m ()
- 12.5% <a href="#m2">[2]</a> 1/1 2 A.m ()
- 12.5% <a href="#m3">[3]</a> 1/1 2 B.m ()
- 12.5% <a href="#m4">[4]</a> 1/1 2 R.m ()
- 12.5% <a href="#m5">[5]</a> 1/1 2 S.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 8 (toplevel)
-[1] 50.0% 2+0 8 Z.m ()
- 100.0% excl 8
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[2] 12.5% 1+0 2 A.m ()
- 100.0% excl 2
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 12.5% 1+0 2 B.m ()
- 100.0% excl 2
-<a name="m4"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[4] 12.5% 1+0 2 R.m ()
- 100.0% excl 2
-<a name="m5"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[5] 12.5% 1+0 2 S.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 8 50.00 50.00 50.00 0.00 0.00 1 main
- 8 50.00 100.00 50.00 0.00 50.00 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 16 (100.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 50.00 50.00 main
- 8 50.00 50.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 8 ( 50.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 0 0.00 50.00 main
- 8 100.00 50.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 8 50.0 50.0 2+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 8 8 100.0 100.0 2+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 2 12.5 62.5 1+0 A</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 12.5 75.0 1+0 B</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span> 2 12.5 87.5 1+0 R</div>
-<div class="parent" id="d3">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m4">[4]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span> 2 12.5 100.0 1+0 S</div>
-<div class="parent" id="d4">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m5">[5]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 16 100.0 100.0 6+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 8 8 50.0 50.0 2+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 62.5 1+0 <a href="#m2">[2]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 75.0 1+0 <a href="#m3">[3]</a> B.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 87.5 1+0 <a href="#m4">[4]</a> R.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 100.0 1+0 <a href="#m5">[5]</a> S.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ _________
-# __|A |___________|B |_____|Z |_______
-#
-# ___________ ____ ____
-# _______|Z |_____|A |_________|B |__
-#
-#
-0 1 A
-2 1 A
-0 2 Z
-4 2 Z
-2 1 B
-4 1 B
-4 2 A
-6 2 A
-4 1 Z
-8 1 Z
-6 2 B
-8 2 B
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 16
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 4 25.00 75.00 <a href="#m2">[2]</a> A.m ()
- 4 25.00 100.00 <a href="#m3">[3]</a> B.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 16 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 2/2 8 Z.m ()
- 25.0% <a href="#m2">[2]</a> 2/2 4 A.m ()
- 25.0% <a href="#m3">[3]</a> 2/2 4 B.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 8 (toplevel)
-[1] 50.0% 2+0 8 Z.m ()
- 100.0% excl 8
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[2] 25.0% 2+0 4 A.m ()
- 100.0% excl 4
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[3] 25.0% 2+0 4 B.m ()
- 100.0% excl 4
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 8 50.00 50.00 50.00 0.00 0.00 1 main
- 8 50.00 100.00 50.00 0.00 0.00 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 16 (100.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 50.00 50.00 main
- 8 50.00 50.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 8 50.0 50.0 2+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 8 8 100.0 100.0 2+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 4 25.0 75.0 2+0 A</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 4 25.0 100.0 2+0 B</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 16 100.0 100.0 6+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 8 8 50.0 50.0 2+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 25.0 75.0 2+0 <a href="#m2">[2]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 25.0 100.0 2+0 <a href="#m3">[3]</a> B.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____ ________ ____ ____ ____
-# __|A ||Z ||B ||Z ||D ||Z ||E |__
-#
-0 1 A
-2 1 A
-2 1 Z
-4 1 Z
-4 1 B
-6 1 B
-6 1 Z
-10 1 Z
-10 1 D
-12 1 D
-12 1 Z
-14 1 Z
-14 1 E
-16 1 E
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 16
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 2 12.50 62.50 <a href="#m2">[2]</a> A.m ()
- 2 12.50 75.00 <a href="#m3">[3]</a> B.m ()
- 2 12.50 87.50 <a href="#m4">[4]</a> D.m ()
- 2 12.50 100.00 <a href="#m5">[5]</a> E.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 16 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 3/3 8 Z.m ()
- 12.5% <a href="#m2">[2]</a> 1/1 2 A.m ()
- 12.5% <a href="#m3">[3]</a> 1/1 2 B.m ()
- 12.5% <a href="#m4">[4]</a> 1/1 2 D.m ()
- 12.5% <a href="#m5">[5]</a> 1/1 2 E.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 3/3 8 (toplevel)
-[1] 50.0% 3+0 8 Z.m ()
- 100.0% excl 8
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[2] 12.5% 1+0 2 A.m ()
- 100.0% excl 2
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 12.5% 1+0 2 B.m ()
- 100.0% excl 2
-<a name="m4"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[4] 12.5% 1+0 2 D.m ()
- 100.0% excl 2
-<a name="m5"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[5] 12.5% 1+0 2 E.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 16 100.00 100.00 37.50 37.50 0.00 1 main
- 0 0.00 100.00 nan nan nan 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 6 ( 37.50% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 6 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 6 ( 37.50% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 6 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 8 50.0 50.0 3+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 8 8 100.0 100.0 3+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 2 12.5 62.5 1+0 A</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 12.5 75.0 1+0 B</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span> 2 12.5 87.5 1+0 D</div>
-<div class="parent" id="d3">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m4">[4]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span> 2 12.5 100.0 1+0 E</div>
-<div class="parent" id="d4">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m5">[5]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 16 100.0 100.0 7+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 8 8 50.0 50.0 3+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 62.5 1+0 <a href="#m2">[2]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 75.0 1+0 <a href="#m3">[3]</a> B.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 87.5 1+0 <a href="#m4">[4]</a> D.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 100.0 1+0 <a href="#m5">[5]</a> E.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____ ________ ____ ____ ____
-# __|R ||Z ||S ||Z ||R ||Z ||S |__
-#
-0 1 R
-2 1 R
-2 1 Z
-4 1 Z
-4 1 S
-6 1 S
-6 1 Z
-10 1 Z
-10 1 R
-12 1 R
-12 1 Z
-14 1 Z
-14 1 S
-16 1 S
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 16
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 4 25.00 75.00 <a href="#m2">[2]</a> R.m ()
- 4 25.00 100.00 <a href="#m3">[3]</a> S.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 16 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 3/3 8 Z.m ()
- 25.0% <a href="#m2">[2]</a> 2/2 4 R.m ()
- 25.0% <a href="#m3">[3]</a> 2/2 4 S.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 3/3 8 (toplevel)
-[1] 50.0% 3+0 8 Z.m ()
- 100.0% excl 8
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[2] 25.0% 2+0 4 R.m ()
- 100.0% excl 4
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[3] 25.0% 2+0 4 S.m ()
- 100.0% excl 4
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 16 100.00 100.00 75.00 0.00 75.00 1 main
- 0 0.00 100.00 nan nan nan 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 12 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 12 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 12 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 12 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 8 50.0 50.0 3+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 8 8 100.0 100.0 3+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 4 25.0 75.0 2+0 R</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 4 25.0 100.0 2+0 S</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 16 100.0 100.0 7+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 8 8 50.0 50.0 3+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 25.0 75.0 2+0 <a href="#m2">[2]</a> R.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 25.0 100.0 2+0 <a href="#m3">[3]</a> S.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____
-# ____ ____ ____ ________ ____|Z |____
-# __|A ||Z ||B ||Z ||C |__
-#
-0 1 A
-2 1 A
-2 1 Z
-4 1 Z
-4 1 B
-6 1 B
-6 1 Z
-10 1 Z
-10 1 C
-12 1 Z
-14 1 Z
-16 1 C
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 16
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 4 25.00 75.00 <a href="#m2">[2]</a> C.m ()
- 2 12.50 87.50 <a href="#m3">[3]</a> A.m ()
- 2 12.50 100.00 <a href="#m4">[4]</a> B.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 16 (toplevel)
- 0.0% excl 0
- 37.5% <a href="#m2">[2]</a> 1/1 6 C.m ()
- 37.5% <a href="#m1">[1]</a> 2/3 6 Z.m ()
- 12.5% <a href="#m3">[3]</a> 1/1 2 A.m ()
- 12.5% <a href="#m4">[4]</a> 1/1 2 B.m ()
-<a name="m1"></a>----------------------------------------------------
- 75.0% <a href="#m0">[0]</a> 2/3 6 (toplevel)
- 25.0% <a href="#m2">[2]</a> 1/3 2 C.m ()
-[1] 50.0% 3+0 8 Z.m ()
- 100.0% excl 8
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 6 (toplevel)
-[2] 37.5% 1+0 6 C.m ()
- 66.7% excl 4
- 33.3% <a href="#m1">[1]</a> 1/3 2 Z.m ()
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 12.5% 1+0 2 A.m ()
- 100.0% excl 2
-<a name="m4"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[4] 12.5% 1+0 2 B.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 16 100.00 100.00 75.00 0.00 0.00 1 main
- 0 0.00 100.00 nan nan nan 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 12 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 12 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 8 50.0 50.0 3+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 8 8 100.0 100.0 3+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 4 25.0 75.0 1+0 C</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 4 6 100.0 100.0 1+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 12.5 87.5 1+0 A</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span> 2 12.5 100.0 1+0 B</div>
-<div class="parent" id="d3">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m4">[4]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 16 100.0 100.0 6+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 8 8 50.0 50.0 3+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 6 25.0 75.0 1+0 <a href="#m2">[2]</a> C.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 87.5 1+0 <a href="#m3">[3]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 100.0 1+0 <a href="#m4">[4]</a> B.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____ ________ ____ ____ ____
-# __|A ||Z ||B ||Z ||A ||Z ||B |__
-#
-0 1 A
-2 1 A
-2 1 Z
-4 1 Z
-4 1 B
-6 1 B
-6 1 Z
-10 1 Z
-10 1 A
-12 1 A
-12 1 Z
-14 1 Z
-14 1 B
-16 1 B
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 16
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 4 25.00 75.00 <a href="#m2">[2]</a> A.m ()
- 4 25.00 100.00 <a href="#m3">[3]</a> B.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 16 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 3/3 8 Z.m ()
- 25.0% <a href="#m2">[2]</a> 2/2 4 A.m ()
- 25.0% <a href="#m3">[3]</a> 2/2 4 B.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 3/3 8 (toplevel)
-[1] 50.0% 3+0 8 Z.m ()
- 100.0% excl 8
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[2] 25.0% 2+0 4 A.m ()
- 100.0% excl 4
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[3] 25.0% 2+0 4 B.m ()
- 100.0% excl 4
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 16 100.00 100.00 75.00 0.00 0.00 1 main
- 0 0.00 100.00 nan nan nan 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 12 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 12 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 8 50.0 50.0 3+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 8 8 100.0 100.0 3+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 4 25.0 75.0 2+0 A</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 4 25.0 100.0 2+0 B</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 16 100.0 100.0 7+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 8 8 50.0 50.0 3+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 25.0 75.0 2+0 <a href="#m2">[2]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 25.0 100.0 2+0 <a href="#m3">[3]</a> B.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ________
-# __|A |_____________________|B ||Z |__
-#
-# ____ ________ ____
-# _______|D ||Z ||E |______________
-#
-#
-0 1 A
-2 1 A
-0 2 D
-2 2 D
-2 2 Z
-6 2 Z
-6 2 E
-8 2 E
-2 1 B
-4 1 B
-4 1 Z
-8 1 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 16
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 2 12.50 62.50 <a href="#m2">[2]</a> A.m ()
- 2 12.50 75.00 <a href="#m3">[3]</a> B.m ()
- 2 12.50 87.50 <a href="#m4">[4]</a> D.m ()
- 2 12.50 100.00 <a href="#m5">[5]</a> E.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 16 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 2/2 8 Z.m ()
- 12.5% <a href="#m2">[2]</a> 1/1 2 A.m ()
- 12.5% <a href="#m3">[3]</a> 1/1 2 B.m ()
- 12.5% <a href="#m4">[4]</a> 1/1 2 D.m ()
- 12.5% <a href="#m5">[5]</a> 1/1 2 E.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 8 (toplevel)
-[1] 50.0% 2+0 8 Z.m ()
- 100.0% excl 8
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[2] 12.5% 1+0 2 A.m ()
- 100.0% excl 2
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 12.5% 1+0 2 B.m ()
- 100.0% excl 2
-<a name="m4"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[4] 12.5% 1+0 2 D.m ()
- 100.0% excl 2
-<a name="m5"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[5] 12.5% 1+0 2 E.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 8 50.00 50.00 50.00 0.00 0.00 1 main
- 8 50.00 100.00 0.00 100.00 0.00 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 12 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 12 100.00 33.33 main
- 0 0.00 66.67 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 8 ( 50.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 0 0.00 0.00 main
- 8 100.00 100.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 8 50.0 50.0 2+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 8 8 100.0 100.0 2+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 2 12.5 62.5 1+0 A</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 12.5 75.0 1+0 B</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span> 2 12.5 87.5 1+0 D</div>
-<div class="parent" id="d3">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m4">[4]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span> 2 12.5 100.0 1+0 E</div>
-<div class="parent" id="d4">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m5">[5]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 16 100.0 100.0 6+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 8 8 50.0 50.0 2+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 62.5 1+0 <a href="#m2">[2]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 75.0 1+0 <a href="#m3">[3]</a> B.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 87.5 1+0 <a href="#m4">[4]</a> D.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 100.0 1+0 <a href="#m5">[5]</a> E.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ________
-# __|R |_____________________|S ||Z |__
-#
-# ____ ________ ____
-# _______|R ||Z ||S |______________
-#
-#
-0 1 R
-2 1 R
-0 2 R
-2 2 R
-2 2 Z
-6 2 Z
-6 2 S
-8 2 S
-2 1 S
-4 1 S
-4 1 Z
-8 1 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 16
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 4 25.00 75.00 <a href="#m2">[2]</a> R.m ()
- 4 25.00 100.00 <a href="#m3">[3]</a> S.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 16 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 2/2 8 Z.m ()
- 25.0% <a href="#m2">[2]</a> 2/2 4 R.m ()
- 25.0% <a href="#m3">[3]</a> 2/2 4 S.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 8 (toplevel)
-[1] 50.0% 2+0 8 Z.m ()
- 100.0% excl 8
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[2] 25.0% 2+0 4 R.m ()
- 100.0% excl 4
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[3] 25.0% 2+0 4 S.m ()
- 100.0% excl 4
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 8 50.00 50.00 50.00 0.00 50.00 1 main
- 8 50.00 100.00 100.00 0.00 100.00 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 12 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 12 100.00 33.33 main
- 8 66.67 66.67 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 12 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 12 100.00 33.33 main
- 8 66.67 66.67 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 8 50.0 50.0 2+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 8 8 100.0 100.0 2+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 4 25.0 75.0 2+0 R</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 4 25.0 100.0 2+0 S</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 16 100.0 100.0 6+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 8 8 50.0 50.0 2+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 25.0 75.0 2+0 <a href="#m2">[2]</a> R.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 25.0 100.0 2+0 <a href="#m3">[3]</a> S.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ________
-# __|A |_____________________|B ||Z |__
-#
-# ____ ________ ____
-# _______|R ||Z ||S |______________
-#
-#
-0 1 A
-2 1 A
-0 2 R
-2 2 R
-2 2 Z
-6 2 Z
-6 2 S
-8 2 S
-2 1 B
-4 1 B
-4 1 Z
-8 1 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 16
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 2 12.50 62.50 <a href="#m2">[2]</a> A.m ()
- 2 12.50 75.00 <a href="#m3">[3]</a> B.m ()
- 2 12.50 87.50 <a href="#m4">[4]</a> R.m ()
- 2 12.50 100.00 <a href="#m5">[5]</a> S.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 16 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 2/2 8 Z.m ()
- 12.5% <a href="#m2">[2]</a> 1/1 2 A.m ()
- 12.5% <a href="#m3">[3]</a> 1/1 2 B.m ()
- 12.5% <a href="#m4">[4]</a> 1/1 2 R.m ()
- 12.5% <a href="#m5">[5]</a> 1/1 2 S.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 8 (toplevel)
-[1] 50.0% 2+0 8 Z.m ()
- 100.0% excl 8
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[2] 12.5% 1+0 2 A.m ()
- 100.0% excl 2
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 12.5% 1+0 2 B.m ()
- 100.0% excl 2
-<a name="m4"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[4] 12.5% 1+0 2 R.m ()
- 100.0% excl 2
-<a name="m5"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[5] 12.5% 1+0 2 S.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 8 50.00 50.00 50.00 0.00 0.00 1 main
- 8 50.00 100.00 100.00 0.00 100.00 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 12 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 12 100.00 33.33 main
- 8 66.67 66.67 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 8 ( 50.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 0 0.00 0.00 main
- 8 100.00 100.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 8 50.0 50.0 2+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 8 8 100.0 100.0 2+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 2 12.5 62.5 1+0 A</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 12.5 75.0 1+0 B</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span> 2 12.5 87.5 1+0 R</div>
-<div class="parent" id="d3">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m4">[4]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span> 2 12.5 100.0 1+0 S</div>
-<div class="parent" id="d4">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m5">[5]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 16 100.0 100.0 6+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 8 8 50.0 50.0 2+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 62.5 1+0 <a href="#m2">[2]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 75.0 1+0 <a href="#m3">[3]</a> B.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 87.5 1+0 <a href="#m4">[4]</a> R.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 100.0 1+0 <a href="#m5">[5]</a> S.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ________
-# __|A |_____________________|B ||Z |__
-#
-# ____ ________ ____
-# _______|A ||Z ||B |______________
-#
-#
-0 1 A
-2 1 A
-0 2 A
-2 2 A
-2 2 Z
-6 2 Z
-6 2 B
-8 2 B
-2 1 B
-4 1 B
-4 1 Z
-8 1 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 16
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 4 25.00 75.00 <a href="#m2">[2]</a> A.m ()
- 4 25.00 100.00 <a href="#m3">[3]</a> B.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 16 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 2/2 8 Z.m ()
- 25.0% <a href="#m2">[2]</a> 2/2 4 A.m ()
- 25.0% <a href="#m3">[3]</a> 2/2 4 B.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 8 (toplevel)
-[1] 50.0% 2+0 8 Z.m ()
- 100.0% excl 8
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[2] 25.0% 2+0 4 A.m ()
- 100.0% excl 4
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[3] 25.0% 2+0 4 B.m ()
- 100.0% excl 4
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 8 50.00 50.00 50.00 0.00 0.00 1 main
- 8 50.00 100.00 100.00 0.00 0.00 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 12 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 12 100.00 33.33 main
- 8 66.67 66.67 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 8 50.0 50.0 2+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 8 8 100.0 100.0 2+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 4 25.0 75.0 2+0 A</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 4 25.0 100.0 2+0 B</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 16 100.0 100.0 6+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 8 8 50.0 50.0 2+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 25.0 75.0 2+0 <a href="#m2">[2]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 25.0 100.0 2+0 <a href="#m3">[3]</a> B.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____ ____ ____
-# __|A ||D ||E ||B ||Z |__
-#
-0 1 A
-2 1 A
-2 1 D
-4 1 D
-4 1 E
-6 1 E
-6 1 B
-8 1 B
-8 1 Z
-10 1 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 10
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 2 20.00 20.00 <a href="#m1">[1]</a> A.m ()
- 2 20.00 40.00 <a href="#m2">[2]</a> B.m ()
- 2 20.00 60.00 <a href="#m3">[3]</a> D.m ()
- 2 20.00 80.00 <a href="#m4">[4]</a> E.m ()
- 2 20.00 100.00 <a href="#m5">[5]</a> Z.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 10 (toplevel)
- 0.0% excl 0
- 20.0% <a href="#m1">[1]</a> 1/1 2 A.m ()
- 20.0% <a href="#m2">[2]</a> 1/1 2 B.m ()
- 20.0% <a href="#m3">[3]</a> 1/1 2 D.m ()
- 20.0% <a href="#m4">[4]</a> 1/1 2 E.m ()
- 20.0% <a href="#m5">[5]</a> 1/1 2 Z.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[1] 20.0% 1+0 2 A.m ()
- 100.0% excl 2
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[2] 20.0% 1+0 2 B.m ()
- 100.0% excl 2
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 20.0% 1+0 2 D.m ()
- 100.0% excl 2
-<a name="m4"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[4] 20.0% 1+0 2 E.m ()
- 100.0% excl 2
-<a name="m5"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[5] 20.0% 1+0 2 Z.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 10 100.00 100.00 80.00 40.00 0.00 1 main
- 0 0.00 100.00 nan nan nan 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 8 ( 80.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 4 ( 40.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 4 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 2 20.0 20.0 1+0 A</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 2 20.0 40.0 1+0 B</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 20.0 60.0 1+0 D</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span> 2 20.0 80.0 1+0 E</div>
-<div class="parent" id="d3">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m4">[4]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span> 2 20.0 100.0 1+0 Z</div>
-<div class="parent" id="d4">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m5">[5]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 10 100.0 100.0 5+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 20.0 1+0 <a href="#m1">[1]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 40.0 1+0 <a href="#m2">[2]</a> B.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 60.0 1+0 <a href="#m3">[3]</a> D.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 80.0 1+0 <a href="#m4">[4]</a> E.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 100.0 1+0 <a href="#m5">[5]</a> Z.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____ ____ ____
-# __|R ||R ||S ||S ||Z |__
-#
-0 1 R
-2 1 R
-2 1 R
-4 1 R
-4 1 S
-6 1 S
-6 1 S
-8 1 S
-8 1 Z
-10 1 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 10
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 4 40.00 40.00 <a href="#m1">[1]</a> R.m ()
- 4 40.00 80.00 <a href="#m2">[2]</a> S.m ()
- 2 20.00 100.00 <a href="#m3">[3]</a> Z.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 10 (toplevel)
- 0.0% excl 0
- 40.0% <a href="#m1">[1]</a> 2/2 4 R.m ()
- 40.0% <a href="#m2">[2]</a> 2/2 4 S.m ()
- 20.0% <a href="#m3">[3]</a> 1/1 2 Z.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[1] 40.0% 2+0 4 R.m ()
- 100.0% excl 4
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[2] 40.0% 2+0 4 S.m ()
- 100.0% excl 4
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 20.0% 1+0 2 Z.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 10 100.00 100.00 80.00 0.00 80.00 1 main
- 0 0.00 100.00 nan nan nan 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 8 ( 80.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 8 ( 80.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 4 40.0 40.0 2+0 R</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 4 40.0 80.0 2+0 S</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 20.0 100.0 1+0 Z</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 10 100.0 100.0 5+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 4 4 40.0 40.0 2+0 <a href="#m1">[1]</a> R.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 40.0 80.0 2+0 <a href="#m2">[2]</a> S.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 100.0 1+0 <a href="#m3">[3]</a> Z.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____ ____
-# __|A ||C ||B ||Z |__
-#
-0 1 A
-2 1 A
-2 1 C
-4 1 C
-4 1 B
-6 1 B
-6 1 Z
-8 1 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 8
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 2 25.00 25.00 <a href="#m1">[1]</a> A.m ()
- 2 25.00 50.00 <a href="#m2">[2]</a> B.m ()
- 2 25.00 75.00 <a href="#m3">[3]</a> C.m ()
- 2 25.00 100.00 <a href="#m4">[4]</a> Z.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 8 (toplevel)
- 0.0% excl 0
- 25.0% <a href="#m1">[1]</a> 1/1 2 A.m ()
- 25.0% <a href="#m2">[2]</a> 1/1 2 B.m ()
- 25.0% <a href="#m3">[3]</a> 1/1 2 C.m ()
- 25.0% <a href="#m4">[4]</a> 1/1 2 Z.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[1] 25.0% 1+0 2 A.m ()
- 100.0% excl 2
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[2] 25.0% 1+0 2 B.m ()
- 100.0% excl 2
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 25.0% 1+0 2 C.m ()
- 100.0% excl 2
-<a name="m4"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[4] 25.0% 1+0 2 Z.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 8 100.00 100.00 75.00 0.00 0.00 1 main
- 0 0.00 100.00 nan nan nan 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 6 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 6 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 2 25.0 25.0 1+0 A</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 2 25.0 50.0 1+0 B</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 25.0 75.0 1+0 C</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span> 2 25.0 100.0 1+0 Z</div>
-<div class="parent" id="d3">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m4">[4]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 8 100.0 100.0 4+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 2 2 25.0 25.0 1+0 <a href="#m1">[1]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 25.0 50.0 1+0 <a href="#m2">[2]</a> B.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 25.0 75.0 1+0 <a href="#m3">[3]</a> C.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 25.0 100.0 1+0 <a href="#m4">[4]</a> Z.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____ ____ ____
-# __|A ||A ||B ||B ||Z |__
-#
-0 1 A
-2 1 A
-2 1 A
-4 1 A
-4 1 B
-6 1 B
-6 1 B
-8 1 B
-8 1 Z
-10 1 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 10
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 4 40.00 40.00 <a href="#m1">[1]</a> A.m ()
- 4 40.00 80.00 <a href="#m2">[2]</a> B.m ()
- 2 20.00 100.00 <a href="#m3">[3]</a> Z.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 10 (toplevel)
- 0.0% excl 0
- 40.0% <a href="#m1">[1]</a> 2/2 4 A.m ()
- 40.0% <a href="#m2">[2]</a> 2/2 4 B.m ()
- 20.0% <a href="#m3">[3]</a> 1/1 2 Z.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[1] 40.0% 2+0 4 A.m ()
- 100.0% excl 4
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[2] 40.0% 2+0 4 B.m ()
- 100.0% excl 4
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 20.0% 1+0 2 Z.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 10 100.00 100.00 80.00 0.00 0.00 1 main
- 0 0.00 100.00 nan nan nan 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 8 ( 80.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 4 40.0 40.0 2+0 A</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 4 40.0 80.0 2+0 B</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 20.0 100.0 1+0 Z</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 10 100.0 100.0 5+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 4 4 40.0 40.0 2+0 <a href="#m1">[1]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 40.0 80.0 2+0 <a href="#m2">[2]</a> B.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 100.0 1+0 <a href="#m3">[3]</a> Z.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____
-# __|A |______|B ||Z |__
-#
-# _____
-# ________|Z |_________________
-#
-0 1 A
-2 1 A
-0 2 Z
-2 2 Z
-2 1 B
-4 1 B
-4 1 Z
-6 1 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 8
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 4 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 2 25.00 75.00 <a href="#m2">[2]</a> A.m ()
- 2 25.00 100.00 <a href="#m3">[3]</a> B.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 8 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 2/2 4 Z.m ()
- 25.0% <a href="#m2">[2]</a> 1/1 2 A.m ()
- 25.0% <a href="#m3">[3]</a> 1/1 2 B.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[1] 50.0% 2+0 4 Z.m ()
- 100.0% excl 4
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[2] 25.0% 1+0 2 A.m ()
- 100.0% excl 2
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 25.0% 1+0 2 B.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 6 75.00 75.00 66.67 0.00 0.00 1 main
- 2 25.00 100.00 0.00 0.00 0.00 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 6 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 6 100.00 66.67 main
- 0 0.00 33.33 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 4 50.0 50.0 2+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 2 25.0 75.0 1+0 A</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 25.0 100.0 1+0 B</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 8 100.0 100.0 4+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 4 4 50.0 50.0 2+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 25.0 75.0 1+0 <a href="#m2">[2]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 25.0 100.0 1+0 <a href="#m3">[3]</a> B.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____ ____
-# __|A ||Z ||B ||Z |__
-#
-0 1 A
-2 1 A
-2 1 Z
-4 1 Z
-4 1 B
-6 1 B
-6 1 Z
-8 1 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 8
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 4 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 2 25.00 75.00 <a href="#m2">[2]</a> A.m ()
- 2 25.00 100.00 <a href="#m3">[3]</a> B.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 8 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 2/2 4 Z.m ()
- 25.0% <a href="#m2">[2]</a> 1/1 2 A.m ()
- 25.0% <a href="#m3">[3]</a> 1/1 2 B.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[1] 50.0% 2+0 4 Z.m ()
- 100.0% excl 4
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[2] 25.0% 1+0 2 A.m ()
- 100.0% excl 2
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 25.0% 1+0 2 B.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 8 100.00 100.00 75.00 0.00 0.00 1 main
- 0 0.00 100.00 nan nan nan 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 6 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 6 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 4 50.0 50.0 2+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 2 25.0 75.0 1+0 A</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 25.0 100.0 1+0 B</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 8 100.0 100.0 4+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 4 4 50.0 50.0 2+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 25.0 75.0 1+0 <a href="#m2">[2]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 25.0 100.0 1+0 <a href="#m3">[3]</a> B.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____ ____
-# __|A |___________|B ||Z |____|Z |_______
-#
-# ____ ____ ____ ____
-# _______|Z ||D |___________|E |____|Z |__
-#
-#
-0 1 A
-2 1 A
-0 2 Z
-2 2 Z
-2 2 D
-4 2 D
-2 1 B
-4 1 B
-4 1 Z
-6 1 Z
-4 2 E
-6 2 E
-6 1 Z
-8 1 Z
-6 2 Z
-8 2 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 16
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 2 12.50 62.50 <a href="#m2">[2]</a> A.m ()
- 2 12.50 75.00 <a href="#m3">[3]</a> B.m ()
- 2 12.50 87.50 <a href="#m4">[4]</a> D.m ()
- 2 12.50 100.00 <a href="#m5">[5]</a> E.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 16 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 4/4 8 Z.m ()
- 12.5% <a href="#m2">[2]</a> 1/1 2 A.m ()
- 12.5% <a href="#m3">[3]</a> 1/1 2 B.m ()
- 12.5% <a href="#m4">[4]</a> 1/1 2 D.m ()
- 12.5% <a href="#m5">[5]</a> 1/1 2 E.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 4/4 8 (toplevel)
-[1] 50.0% 4+0 8 Z.m ()
- 100.0% excl 8
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[2] 12.5% 1+0 2 A.m ()
- 100.0% excl 2
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 12.5% 1+0 2 B.m ()
- 100.0% excl 2
-<a name="m4"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[4] 12.5% 1+0 2 D.m ()
- 100.0% excl 2
-<a name="m5"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[5] 12.5% 1+0 2 E.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 8 50.00 50.00 50.00 0.00 0.00 1 main
- 8 50.00 100.00 0.00 50.00 0.00 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 8 ( 50.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 100.00 50.00 main
- 0 0.00 50.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 8 ( 50.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 0 0.00 50.00 main
- 8 100.00 50.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 8 50.0 50.0 4+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 8 8 100.0 100.0 4+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 2 12.5 62.5 1+0 A</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 12.5 75.0 1+0 B</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span> 2 12.5 87.5 1+0 D</div>
-<div class="parent" id="d3">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m4">[4]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span> 2 12.5 100.0 1+0 E</div>
-<div class="parent" id="d4">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m5">[5]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 16 100.0 100.0 8+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 8 8 50.0 50.0 4+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 62.5 1+0 <a href="#m2">[2]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 75.0 1+0 <a href="#m3">[3]</a> B.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 87.5 1+0 <a href="#m4">[4]</a> D.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 100.0 1+0 <a href="#m5">[5]</a> E.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____ ____
-# __|R |___________|S ||Z |____|Z |_______
-#
-# ____ ____ ____ ____
-# _______|Z ||R |___________|S |____|Z |__
-#
-#
-0 1 R
-2 1 R
-0 2 Z
-2 2 Z
-2 2 R
-4 2 R
-2 1 S
-4 1 S
-4 1 Z
-6 1 Z
-4 2 S
-6 2 S
-6 1 Z
-8 1 Z
-6 2 Z
-8 2 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 16
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 4 25.00 75.00 <a href="#m2">[2]</a> R.m ()
- 4 25.00 100.00 <a href="#m3">[3]</a> S.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 16 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 4/4 8 Z.m ()
- 25.0% <a href="#m2">[2]</a> 2/2 4 R.m ()
- 25.0% <a href="#m3">[3]</a> 2/2 4 S.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 4/4 8 (toplevel)
-[1] 50.0% 4+0 8 Z.m ()
- 100.0% excl 8
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[2] 25.0% 2+0 4 R.m ()
- 100.0% excl 4
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[3] 25.0% 2+0 4 S.m ()
- 100.0% excl 4
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 8 50.00 50.00 50.00 0.00 50.00 1 main
- 8 50.00 100.00 50.00 0.00 50.00 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 12 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 66.67 50.00 main
- 8 66.67 50.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 12 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 66.67 50.00 main
- 8 66.67 50.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 8 50.0 50.0 4+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 8 8 100.0 100.0 4+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 4 25.0 75.0 2+0 R</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 4 25.0 100.0 2+0 S</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 16 100.0 100.0 8+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 8 8 50.0 50.0 4+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 25.0 75.0 2+0 <a href="#m2">[2]</a> R.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 25.0 100.0 2+0 <a href="#m3">[3]</a> S.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____ ____
-# __|A |___________|B ||Z |____|Z |_______
-#
-# ____ ____ ____ ____
-# _______|Z ||R |___________|S |____|Z |__
-#
-#
-0 1 A
-2 1 A
-0 2 Z
-2 2 Z
-2 2 R
-4 2 R
-2 1 B
-4 1 B
-4 1 Z
-6 1 Z
-4 2 S
-6 2 S
-6 1 Z
-8 1 Z
-6 2 Z
-8 2 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 16
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 2 12.50 62.50 <a href="#m2">[2]</a> A.m ()
- 2 12.50 75.00 <a href="#m3">[3]</a> B.m ()
- 2 12.50 87.50 <a href="#m4">[4]</a> R.m ()
- 2 12.50 100.00 <a href="#m5">[5]</a> S.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 16 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 4/4 8 Z.m ()
- 12.5% <a href="#m2">[2]</a> 1/1 2 A.m ()
- 12.5% <a href="#m3">[3]</a> 1/1 2 B.m ()
- 12.5% <a href="#m4">[4]</a> 1/1 2 R.m ()
- 12.5% <a href="#m5">[5]</a> 1/1 2 S.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 4/4 8 (toplevel)
-[1] 50.0% 4+0 8 Z.m ()
- 100.0% excl 8
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[2] 12.5% 1+0 2 A.m ()
- 100.0% excl 2
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 12.5% 1+0 2 B.m ()
- 100.0% excl 2
-<a name="m4"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[4] 12.5% 1+0 2 R.m ()
- 100.0% excl 2
-<a name="m5"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[5] 12.5% 1+0 2 S.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 8 50.00 50.00 50.00 0.00 0.00 1 main
- 8 50.00 100.00 50.00 0.00 50.00 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 12 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 66.67 50.00 main
- 8 66.67 50.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 8 ( 50.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 0 0.00 50.00 main
- 8 100.00 50.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 8 50.0 50.0 4+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 8 8 100.0 100.0 4+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 2 12.5 62.5 1+0 A</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 12.5 75.0 1+0 B</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span> 2 12.5 87.5 1+0 R</div>
-<div class="parent" id="d3">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m4">[4]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span> 2 12.5 100.0 1+0 S</div>
-<div class="parent" id="d4">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m5">[5]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 16 100.0 100.0 8+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 8 8 50.0 50.0 4+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 62.5 1+0 <a href="#m2">[2]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 75.0 1+0 <a href="#m3">[3]</a> B.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 87.5 1+0 <a href="#m4">[4]</a> R.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 12.5 100.0 1+0 <a href="#m5">[5]</a> S.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____ ____
-# __|A |___________|B ||Z |____|Z |_______
-#
-# ____ ____ ____ ____
-# _______|Z ||A |___________|B |____|Z |__
-#
-#
-0 1 A
-2 1 A
-0 2 Z
-2 2 Z
-2 2 A
-4 2 A
-2 1 B
-4 1 B
-4 1 Z
-6 1 Z
-4 2 B
-6 2 B
-6 1 Z
-8 1 Z
-6 2 Z
-8 2 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 16
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 8 50.00 50.00 <a href="#m1">[1]</a> Z.m ()
- 4 25.00 75.00 <a href="#m2">[2]</a> A.m ()
- 4 25.00 100.00 <a href="#m3">[3]</a> B.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 16 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 4/4 8 Z.m ()
- 25.0% <a href="#m2">[2]</a> 2/2 4 A.m ()
- 25.0% <a href="#m3">[3]</a> 2/2 4 B.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 4/4 8 (toplevel)
-[1] 50.0% 4+0 8 Z.m ()
- 100.0% excl 8
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[2] 25.0% 2+0 4 A.m ()
- 100.0% excl 4
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[3] 25.0% 2+0 4 B.m ()
- 100.0% excl 4
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 8 50.00 50.00 50.00 0.00 0.00 1 main
- 8 50.00 100.00 50.00 0.00 0.00 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 12 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 66.67 50.00 main
- 8 66.67 50.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 8 50.0 50.0 4+0 Z</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 8 8 100.0 100.0 4+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 4 25.0 75.0 2+0 A</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 4 25.0 100.0 2+0 B</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 16 100.0 100.0 8+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 8 8 50.0 50.0 4+0 <a href="#m1">[1]</a> Z.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 25.0 75.0 2+0 <a href="#m2">[2]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 25.0 100.0 2+0 <a href="#m3">[3]</a> B.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____ ____ ____
-# __|A ||D ||B ||E ||Z |__
-#
-0 1 A
-2 1 A
-2 1 D
-4 1 D
-4 1 B
-6 1 B
-6 1 E
-8 1 E
-8 1 Z
-10 1 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 10
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 2 20.00 20.00 <a href="#m1">[1]</a> A.m ()
- 2 20.00 40.00 <a href="#m2">[2]</a> B.m ()
- 2 20.00 60.00 <a href="#m3">[3]</a> D.m ()
- 2 20.00 80.00 <a href="#m4">[4]</a> E.m ()
- 2 20.00 100.00 <a href="#m5">[5]</a> Z.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 10 (toplevel)
- 0.0% excl 0
- 20.0% <a href="#m1">[1]</a> 1/1 2 A.m ()
- 20.0% <a href="#m2">[2]</a> 1/1 2 B.m ()
- 20.0% <a href="#m3">[3]</a> 1/1 2 D.m ()
- 20.0% <a href="#m4">[4]</a> 1/1 2 E.m ()
- 20.0% <a href="#m5">[5]</a> 1/1 2 Z.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[1] 20.0% 1+0 2 A.m ()
- 100.0% excl 2
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[2] 20.0% 1+0 2 B.m ()
- 100.0% excl 2
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 20.0% 1+0 2 D.m ()
- 100.0% excl 2
-<a name="m4"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[4] 20.0% 1+0 2 E.m ()
- 100.0% excl 2
-<a name="m5"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[5] 20.0% 1+0 2 Z.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 10 100.00 100.00 60.00 60.00 0.00 1 main
- 0 0.00 100.00 nan nan nan 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 6 ( 60.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 6 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 6 ( 60.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 6 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 2 20.0 20.0 1+0 A</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 2 20.0 40.0 1+0 B</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 20.0 60.0 1+0 D</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span> 2 20.0 80.0 1+0 E</div>
-<div class="parent" id="d3">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m4">[4]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d4')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd4">+</span> 2 20.0 100.0 1+0 Z</div>
-<div class="parent" id="d4">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m5">[5]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 10 100.0 100.0 5+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 20.0 1+0 <a href="#m1">[1]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 40.0 1+0 <a href="#m2">[2]</a> B.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 60.0 1+0 <a href="#m3">[3]</a> D.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 80.0 1+0 <a href="#m4">[4]</a> E.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 100.0 1+0 <a href="#m5">[5]</a> Z.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____ ____ ____
-# __|R ||R ||S ||S ||Z |__
-#
-0 1 R
-2 1 R
-2 1 R
-4 1 R
-4 1 S
-6 1 S
-6 1 S
-8 1 S
-8 1 Z
-10 1 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 10
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 4 40.00 40.00 <a href="#m1">[1]</a> R.m ()
- 4 40.00 80.00 <a href="#m2">[2]</a> S.m ()
- 2 20.00 100.00 <a href="#m3">[3]</a> Z.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 10 (toplevel)
- 0.0% excl 0
- 40.0% <a href="#m1">[1]</a> 2/2 4 R.m ()
- 40.0% <a href="#m2">[2]</a> 2/2 4 S.m ()
- 20.0% <a href="#m3">[3]</a> 1/1 2 Z.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[1] 40.0% 2+0 4 R.m ()
- 100.0% excl 4
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[2] 40.0% 2+0 4 S.m ()
- 100.0% excl 4
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 20.0% 1+0 2 Z.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 10 100.00 100.00 80.00 0.00 80.00 1 main
- 0 0.00 100.00 nan nan nan 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 8 ( 80.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 8 ( 80.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 4 40.0 40.0 2+0 R</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 4 40.0 80.0 2+0 S</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 20.0 100.0 1+0 Z</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 10 100.0 100.0 5+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 4 4 40.0 40.0 2+0 <a href="#m1">[1]</a> R.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 40.0 80.0 2+0 <a href="#m2">[2]</a> S.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 100.0 1+0 <a href="#m3">[3]</a> Z.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____
-# ____ ____|B |____ ____
-# __|A ||C ||Z |__
-#
-0 1 A
-2 1 A
-2 1 C
-4 1 B
-6 1 B
-8 1 C
-8 1 Z
-10 1 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 10
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 4 40.00 40.00 <a href="#m1">[1]</a> C.m ()
- 2 20.00 60.00 <a href="#m2">[2]</a> A.m ()
- 2 20.00 80.00 <a href="#m3">[3]</a> B.m ()
- 2 20.00 100.00 <a href="#m4">[4]</a> Z.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 10 (toplevel)
- 0.0% excl 0
- 60.0% <a href="#m1">[1]</a> 1/1 6 C.m ()
- 20.0% <a href="#m2">[2]</a> 1/1 2 A.m ()
- 20.0% <a href="#m4">[4]</a> 1/1 2 Z.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 6 (toplevel)
-[1] 60.0% 1+0 6 C.m ()
- 66.7% excl 4
- 33.3% <a href="#m3">[3]</a> 1/1 2 B.m ()
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[2] 20.0% 1+0 2 A.m ()
- 100.0% excl 2
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m1">[1]</a> 1/1 2 C.m ()
-[3] 20.0% 1+0 2 B.m ()
- 100.0% excl 2
-<a name="m4"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[4] 20.0% 1+0 2 Z.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 10 100.00 100.00 80.00 0.00 0.00 1 main
- 0 0.00 100.00 nan nan nan 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 8 ( 80.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 4 40.0 40.0 1+0 C</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 4 6 100.0 100.0 1+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 2 20.0 60.0 1+0 A</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 20.0 80.0 1+0 B</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d3')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd3">+</span> 2 20.0 100.0 1+0 Z</div>
-<div class="parent" id="d3">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m4">[4]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 10 100.0 100.0 4+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 4 6 40.0 40.0 1+0 <a href="#m1">[1]</a> C.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 60.0 1+0 <a href="#m2">[2]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 80.0 1+0 <a href="#m3">[3]</a> B.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 100.0 1+0 <a href="#m4">[4]</a> Z.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____ ____ ____
-# __|A ||A ||B ||B ||Z |__
-#
-0 1 A
-2 1 A
-2 1 A
-4 1 A
-4 1 B
-6 1 B
-6 1 B
-8 1 B
-8 1 Z
-10 1 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 10
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 4 40.00 40.00 <a href="#m1">[1]</a> A.m ()
- 4 40.00 80.00 <a href="#m2">[2]</a> B.m ()
- 2 20.00 100.00 <a href="#m3">[3]</a> Z.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 10 (toplevel)
- 0.0% excl 0
- 40.0% <a href="#m1">[1]</a> 2/2 4 A.m ()
- 40.0% <a href="#m2">[2]</a> 2/2 4 B.m ()
- 20.0% <a href="#m3">[3]</a> 1/1 2 Z.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[1] 40.0% 2+0 4 A.m ()
- 100.0% excl 4
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[2] 40.0% 2+0 4 B.m ()
- 100.0% excl 4
-<a name="m3"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 2 (toplevel)
-[3] 20.0% 1+0 2 Z.m ()
- 100.0% excl 2
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 10 100.00 100.00 80.00 0.00 0.00 1 main
- 0 0.00 100.00 nan nan nan 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 8 ( 80.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 8 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 4 40.0 40.0 2+0 A</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 4 40.0 80.0 2+0 B</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d2')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd2">+</span> 2 20.0 100.0 1+0 Z</div>
-<div class="parent" id="d2">
-<div class="leaf"><span class="leaf"> </span> 2 2 100.0 100.0 1+0 <a href="#m3">[3]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 10 100.0 100.0 5+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 4 4 40.0 40.0 2+0 <a href="#m1">[1]</a> A.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 40.0 80.0 2+0 <a href="#m2">[2]</a> B.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 2 2 20.0 100.0 1+0 <a href="#m3">[3]</a> Z.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# ____ ____ ____
-# __|C ||Z |__
-#
-# ____
-# ________|Z |_____________
-#
-0 1 C
-0 2 Z
-2 2 Z
-4 1 C
-4 1 Z
-6 1 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 8
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 4 50.00 50.00 <a href="#m1">[1]</a> C.m ()
- 4 50.00 100.00 <a href="#m2">[2]</a> Z.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 8 (toplevel)
- 0.0% excl 0
- 50.0% <a href="#m1">[1]</a> 1/1 4 C.m ()
- 50.0% <a href="#m2">[2]</a> 2/2 4 Z.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 4 (toplevel)
-[1] 50.0% 1+0 4 C.m ()
- 100.0% excl 4
-<a name="m2"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 2/2 4 (toplevel)
-[2] 50.0% 2+0 4 Z.m ()
- 100.0% excl 4
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 6 75.00 75.00 66.67 0.00 0.00 1 main
- 2 25.00 100.00 0.00 0.00 0.00 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 6 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 6 100.00 66.67 main
- 0 0.00 33.33 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 4 50.0 50.0 1+0 C</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 1+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 4 50.0 100.0 2+0 Z</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 8 100.0 100.0 3+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 4 4 50.0 50.0 1+0 <a href="#m1">[1]</a> C.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 50.0 100.0 2+0 <a href="#m2">[2]</a> Z.m ()</div>
-</div>
-
-</body>
-</html>
+++ /dev/null
-# _____
-# ____|Z |____ ____
-# __|C ||Z |__
-#
-0 1 C
-2 1 Z
-4 1 Z
-6 1 C
-6 1 Z
-8 1 Z
+++ /dev/null
-<html>
-<head>
-<script type="text/javascript" src="(null)sortable.js"></script>
-<script langugage="javascript">
-function toggle(item) {
- obj=document.getElementById(item);
- visible=(obj.style.display!="none" && obj.style.display!="");
- key=document.getElementById("x" + item);
- if (visible) {
- obj.style.display="none";
- key.innerHTML="+";
- } else {
- obj.style.display="block";
- key.innerHTML="-";
- }
-}
-function onMouseOver(obj) {
- obj.style.background="lightblue";
-}
-function onMouseOut(obj) {
- obj.style.background="white";
-}
-</script>
-<style type="text/css">
-div { font-family: courier; font-size: 13 }
-div.parent { margin-left: 15; display: none }
-div.leaf { margin-left: 10 }
-div.header { margin-left: 10 }
-div.link { margin-left: 10; cursor: move }
-span.parent { padding-right: 10; }
-span.leaf { padding-right: 10; }
-a img { border: 0;}
-table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}
-a { text-decoration: none; }
-a:hover { text-decoration: underline; }
-table.sortable th, table.sortable td { text-align: left;}table.sortable tr.odd td { background-color: #ddd; }
-table.sortable tr.even td { background-color: #fff; }
-</style>
-</head><body>
-
-<a name="contents"></a>
-<h2>Table of Contents</h2>
-<ul>
- <li><a href="#exclusive">Exclusive profile</a></li>
- <li><a href="#inclusive">Inclusive profile</a></li>
- <li><a href="#thread">Thread profile</a></li>
- <li><a href="#class">Class/method profile</a></li>
- <li><a href="#method">Method/class profile</a></li>
-</ul>
-
-<a name="exclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-Total cycles: 8
-
-<br><br>
-Exclusive elapsed times for each method, not including time spent in
-children, sorted by exclusive time.
-
-<br><br>
-<pre>
- Usecs self % sum % Method
- 4 50.00 50.00 <a href="#m1">[1]</a> C.m ()
- 4 50.00 100.00 <a href="#m2">[2]</a> Z.m ()
-</pre>
-<a name="inclusive"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Inclusive elapsed times for each method and its parents and children,
-sorted by inclusive time.
-
-<br><br>
-<pre>
-index %/total %/self index calls usecs name
-<a name="m0"></a>----------------------------------------------------
-[0] 100.0% 0+0 8 (toplevel)
- 0.0% excl 0
- 75.0% <a href="#m1">[1]</a> 1/1 6 C.m ()
- 25.0% <a href="#m2">[2]</a> 1/2 2 Z.m ()
-<a name="m1"></a>----------------------------------------------------
- 100.0% <a href="#m0">[0]</a> 1/1 6 (toplevel)
-[1] 75.0% 1+0 6 C.m ()
- 66.7% excl 4
- 33.3% <a href="#m2">[2]</a> 1/2 2 Z.m ()
-<a name="m2"></a>----------------------------------------------------
- 50.0% <a href="#m0">[0]</a> 1/2 2 (toplevel)
- 50.0% <a href="#m1">[1]</a> 1/2 2 C.m ()
-[2] 50.0% 2+0 4 Z.m ()
- 100.0% excl 4
-</pre>
-<a name="thread"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Elapsed times for each thread, sorted by elapsed time.
-Also includes percentage of time spent during the <i>execution</i> of any filters.
-
-<br><br>
-<pre>
- Usecs self % sum % FirstFilter % SecondFilter % RepeatedFilter % tid ThreadName
- 8 100.00 100.00 75.00 0.00 0.00 1 main
- 0 0.00 100.00 nan nan nan 2 foo
- 0 0.00 100.00 nan nan nan 3 bar
- 0 0.00 100.00 nan nan nan 4 blah
-</pre><br />
-
-Break-down of portion of time spent by each thread while waiting on a filter method.
-<br/><br/>
-<pre>
-Filter: FirstFilter
-Total waiting cycles: 6 ( 75.00% of total)
-Details:
-
- Waiting cycles % of total waiting time execution time while waiting thread name
- 6 100.00 100.00 main
- 0 0.00 0.00 foo
- 0 0.00 0.00 bar
- 0 0.00 0.00 blah
-</pre>
-<br/><br/>
-<pre>
-Filter: SecondFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<br/><br/>
-<pre>
-Filter: RepeatedFilter
-Total waiting cycles: 0 ( 0.00% of total)
-</pre>
-<a name="class"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each class, summed over all the methods
-in the class.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Class</div>
-<div class="link" onClick="javascript:toggle('d0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd0">+</span> 4 50.0 50.0 1+0 C</div>
-<div class="parent" id="d0">
-<div class="leaf"><span class="leaf"> </span> 4 6 100.0 100.0 1+0 <a href="#m1">[1]</a> m ()</div>
-</div>
-<div class="link" onClick="javascript:toggle('d1')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xd1">+</span> 4 50.0 100.0 2+0 Z</div>
-<div class="parent" id="d1">
-<div class="leaf"><span class="leaf"> </span> 4 4 100.0 100.0 2+0 <a href="#m2">[2]</a> m ()</div>
-</div>
-<a name="method"></a>
-<hr>
-<a href="#contents">[Top]</a>
-<a href="#exclusive">[Exclusive]</a>
-<a href="#inclusive">[Inclusive]</a>
-<a href="#thread">[Thread]</a>
-<a href="#class">[Class]</a>
-<a href="#method">[Method]</a>
-<br><br>
-
-Exclusive elapsed time for each method, summed over all the classes
-that contain a method with the same name.
-
-<br><br>
-<div class="header"><span class="parent"> </span> Cycles %/total Cumul.% Calls+Recur Method</div>
-<div class="link" onClick="javascript:toggle('e0')" onMouseOver="javascript:onMouseOver(this)" onMouseOut="javascript:onMouseOut(this)"><span class="parent" id="xe0">+</span> 8 100.0 100.0 3+0 m</div>
-<div class="parent" id="e0">
-<div class="leaf"><span class="leaf"> </span> 4 6 50.0 50.0 1+0 <a href="#m1">[1]</a> C.m ()</div>
-<div class="leaf"><span class="leaf"> </span> 4 4 50.0 100.0 2+0 <a href="#m2">[2]</a> Z.m ()</div>
-</div>
-
-</body>
-</html>
const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
const Method* method = saveArea->method;
- if (!dvmIsBreakFrame(fp)) {
+ if (!dvmIsBreakFrame((u4*) fp)) {
pRec->stackElem[stackDepth].method = method;
if (dvmIsNativeMethod(method)) {
pRec->stackElem[stackDepth].pc = 0;
--- /dev/null
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Implementation of an expandable bit vector.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+
+#define kBitVectorGrowth 4 /* increase by 4 u4s when limit hit */
+
+
+/*
+ * Allocate a bit vector with enough space to hold at least the specified
+ * number of bits.
+ */
+BitVector* dvmAllocBitVector(unsigned int startBits, bool expandable)
+{
+ BitVector* bv;
+ unsigned int count;
+
+ assert(sizeof(bv->storage[0]) == 4); /* assuming 32-bit units */
+
+ bv = (BitVector*) malloc(sizeof(BitVector));
+
+ count = (startBits + 31) >> 5;
+
+ bv->storageSize = count;
+ bv->expandable = expandable;
+ bv->storage = (u4*) malloc(count * sizeof(u4));
+ memset(bv->storage, 0x00, count * sizeof(u4));
+ return bv;
+}
+
+/*
+ * Free a BitVector.
+ */
+void dvmFreeBitVector(BitVector* pBits)
+{
+ if (pBits == NULL)
+ return;
+
+ free(pBits->storage);
+ free(pBits);
+}
+
+/*
+ * "Allocate" the first-available bit in the bitmap.
+ *
+ * This is not synchronized. The caller is expected to hold some sort of
+ * lock that prevents multiple threads from executing simultaneously in
+ * dvmAllocBit/dvmFreeBit.
+ */
+int dvmAllocBit(BitVector* pBits)
+{
+ unsigned int word, bit;
+
+retry:
+ for (word = 0; word < pBits->storageSize; word++) {
+ if (pBits->storage[word] != 0xffffffff) {
+ /*
+ * There are unallocated bits in this word. Return the first.
+ */
+ bit = ffs(~(pBits->storage[word])) -1;
+ assert(bit < 32);
+ pBits->storage[word] |= 1 << bit;
+ return (word << 5) | bit;
+ }
+ }
+
+ /*
+ * Ran out of space, allocate more if we're allowed to.
+ */
+ if (!pBits->expandable)
+ return -1;
+
+ pBits->storage = (u4*)realloc(pBits->storage,
+ (pBits->storageSize + kBitVectorGrowth) * sizeof(u4));
+ memset(&pBits->storage[pBits->storageSize], 0x00,
+ kBitVectorGrowth * sizeof(u4));
+ pBits->storageSize += kBitVectorGrowth;
+ goto retry;
+}
+
+/*
+ * Mark the specified bit as "set".
+ */
+void dvmSetBit(BitVector* pBits, unsigned int num)
+{
+ if (num >= pBits->storageSize * sizeof(u4) * 8) {
+ if (!pBits->expandable) {
+ LOGE("Attempt to set bit outside valid range (%d, limit is %d)\n",
+ num, pBits->storageSize * sizeof(u4) * 8);
+ dvmAbort();
+ }
+
+ /* Round up to word boundaries for "num+1" bits */
+ unsigned int newSize = (num + 1 + 31) >> 5;
+ assert(newSize > pBits->storageSize);
+ pBits->storage = (u4*)realloc(pBits->storage, newSize * sizeof(u4));
+ if (pBits->storage == NULL) {
+ LOGE("BitVector expansion to %d failed\n", newSize * sizeof(u4));
+ dvmAbort();
+ }
+ memset(&pBits->storage[pBits->storageSize], 0x00,
+ (newSize - pBits->storageSize) * sizeof(u4));
+ pBits->storageSize = newSize;
+ }
+
+ pBits->storage[num >> 5] |= 1 << (num & 0x1f);
+}
+
+/*
+ * Mark the specified bit as "clear".
+ */
+void dvmClearBit(BitVector* pBits, unsigned int num)
+{
+ assert(num < pBits->storageSize * sizeof(u4) * 8);
+
+ pBits->storage[num >> 5] &= ~(1 << (num & 0x1f));
+}
+
+/*
+ * Mark all bits bit as "clear".
+ */
+void dvmClearAllBits(BitVector* pBits)
+{
+ unsigned int count = pBits->storageSize;
+ memset(pBits->storage, 0, count * sizeof(u4));
+}
+
+/*
+ * Mark specified number of bits as "set". Cannot set all bits like ClearAll
+ * since there might be unused bits - setting those to one will confuse the
+ * iterator.
+ */
+void dvmSetInitialBits(BitVector* pBits, unsigned int numBits)
+{
+ unsigned int idx;
+ assert(((numBits + 31) >> 5) <= pBits->storageSize);
+ for (idx = 0; idx < (numBits >> 5); idx++) {
+ pBits->storage[idx] = -1;
+ }
+ unsigned int remNumBits = numBits & 0x1f;
+ if (remNumBits) {
+ pBits->storage[idx] = (1 << remNumBits) - 1;
+ }
+}
+
+/*
+ * Determine whether or not the specified bit is set.
+ */
+bool dvmIsBitSet(const BitVector* pBits, unsigned int num)
+{
+ assert(num < pBits->storageSize * sizeof(u4) * 8);
+
+ unsigned int val = pBits->storage[num >> 5] & (1 << (num & 0x1f));
+ return (val != 0);
+}
+
+/*
+ * Count the number of bits that are set.
+ */
+int dvmCountSetBits(const BitVector* pBits)
+{
+ unsigned int word;
+ unsigned int count = 0;
+
+ for (word = 0; word < pBits->storageSize; word++) {
+ u4 val = pBits->storage[word];
+
+ if (val != 0) {
+ if (val == 0xffffffff) {
+ count += 32;
+ } else {
+ /* count the number of '1' bits */
+ while (val != 0) {
+ val &= val - 1;
+ count++;
+ }
+ }
+ }
+ }
+
+ return count;
+}
+
+/*
+ * If the vector sizes don't match, log an error and abort.
+ */
+static void checkSizes(const BitVector* bv1, const BitVector* bv2)
+{
+ if (bv1->storageSize != bv2->storageSize) {
+ LOGE("Mismatched vector sizes (%d, %d)\n",
+ bv1->storageSize, bv2->storageSize);
+ dvmAbort();
+ }
+}
+
+/*
+ * Copy a whole vector to the other. Only do that when the both vectors have
+ * the same size.
+ */
+void dvmCopyBitVector(BitVector *dest, const BitVector *src)
+{
+ /* if dest is expandable and < src, we could expand dest to match */
+ checkSizes(dest, src);
+
+ memcpy(dest->storage, src->storage, sizeof(u4) * dest->storageSize);
+}
+
+/*
+ * Intersect two bit vectors and store the result to the dest vector.
+ */
+bool dvmIntersectBitVectors(BitVector *dest, const BitVector *src1,
+ const BitVector *src2)
+{
+ if (dest->storageSize != src1->storageSize ||
+ dest->storageSize != src2->storageSize ||
+ dest->expandable != src1->expandable ||
+ dest->expandable != src2->expandable)
+ return false;
+
+ unsigned int idx;
+ for (idx = 0; idx < dest->storageSize; idx++) {
+ dest->storage[idx] = src1->storage[idx] & src2->storage[idx];
+ }
+ return true;
+}
+
+/*
+ * Unify two bit vectors and store the result to the dest vector.
+ */
+bool dvmUnifyBitVectors(BitVector *dest, const BitVector *src1,
+ const BitVector *src2)
+{
+ if (dest->storageSize != src1->storageSize ||
+ dest->storageSize != src2->storageSize ||
+ dest->expandable != src1->expandable ||
+ dest->expandable != src2->expandable)
+ return false;
+
+ unsigned int idx;
+ for (idx = 0; idx < dest->storageSize; idx++) {
+ dest->storage[idx] = src1->storage[idx] | src2->storage[idx];
+ }
+ return true;
+}
+
+/*
+ * Compare two bit vectors and return true if difference is seen.
+ */
+bool dvmCompareBitVectors(const BitVector *src1, const BitVector *src2)
+{
+ if (src1->storageSize != src2->storageSize ||
+ src1->expandable != src2->expandable)
+ return true;
+
+ unsigned int idx;
+ for (idx = 0; idx < src1->storageSize; idx++) {
+ if (src1->storage[idx] != src2->storage[idx]) return true;
+ }
+ return false;
+}
+
+/* Initialize the iterator structure */
+void dvmBitVectorIteratorInit(BitVector* pBits, BitVectorIterator* iterator)
+{
+ iterator->pBits = pBits;
+ iterator->bitSize = pBits->storageSize * sizeof(u4) * 8;
+ iterator->idx = 0;
+}
+
+/* Return the next position set to 1. -1 means end-of-element reached */
+int dvmBitVectorIteratorNext(BitVectorIterator* iterator)
+{
+ const BitVector* pBits = iterator->pBits;
+ u4 bitIndex = iterator->idx;
+
+ assert(iterator->bitSize == pBits->storageSize * sizeof(u4) * 8);
+ if (bitIndex >= iterator->bitSize) return -1;
+
+ for (; bitIndex < iterator->bitSize; bitIndex++) {
+ unsigned int wordIndex = bitIndex >> 5;
+ unsigned int mask = 1 << (bitIndex & 0x1f);
+ if (pBits->storage[wordIndex] & mask) {
+ iterator->idx = bitIndex+1;
+ return bitIndex;
+ }
+ }
+ /* No more set bits */
+ return -1;
+}
+
+
+/*
+ * Merge the contents of "src" into "dst", checking to see if this causes
+ * any changes to occur. This is a logical OR.
+ *
+ * Returns "true" if the contents of the destination vector were modified.
+ */
+bool dvmCheckMergeBitVectors(BitVector* dst, const BitVector* src)
+{
+ bool changed = false;
+
+ checkSizes(dst, src);
+
+ unsigned int idx;
+ for (idx = 0; idx < dst->storageSize; idx++) {
+ u4 merged = src->storage[idx] | dst->storage[idx];
+ if (dst->storage[idx] != merged) {
+ dst->storage[idx] = merged;
+ changed = true;
+ }
+ }
+
+ return changed;
+}
--- /dev/null
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Miscellaneous utility functions.
+ */
+#ifndef _DALVIK_BITVECTOR
+#define _DALVIK_BITVECTOR
+
+/*
+ * Expanding bitmap, used for tracking resources. Bits are numbered starting
+ * from zero.
+ *
+ * All operations on a BitVector are unsynchronized.
+ */
+typedef struct BitVector {
+ bool expandable; /* expand bitmap if we run out? */
+ u4 storageSize; /* current size, in 32-bit words */
+ u4* storage;
+} BitVector;
+
+/* Handy iterator to walk through the bit positions set to 1 */
+typedef struct BitVectorIterator {
+ BitVector *pBits;
+ u4 idx;
+ u4 bitSize;
+} BitVectorIterator;
+
+/* allocate a bit vector with enough space to hold "startBits" bits */
+BitVector* dvmAllocBitVector(unsigned int startBits, bool expandable);
+void dvmFreeBitVector(BitVector* pBits);
+
+/*
+ * dvmAllocBit always allocates the first possible bit. If we run out of
+ * space in the bitmap, and it's not marked expandable, dvmAllocBit
+ * returns -1.
+ *
+ * dvmSetBit sets the specified bit, expanding the vector if necessary
+ * (and possible). Attempting to set a bit past the limit of a non-expandable
+ * bit vector will cause a fatal error.
+ *
+ * dvmSetInitialBits sets all bits in [0..numBits-1]. Won't expand the vector.
+ *
+ * dvmIsBitSet returns "true" if the bit is set.
+ */
+int dvmAllocBit(BitVector* pBits);
+void dvmSetBit(BitVector* pBits, unsigned int num);
+void dvmClearBit(BitVector* pBits, unsigned int num);
+void dvmClearAllBits(BitVector* pBits);
+void dvmSetInitialBits(BitVector* pBits, unsigned int numBits);
+bool dvmIsBitSet(const BitVector* pBits, unsigned int num);
+
+/* count the number of bits that have been set */
+int dvmCountSetBits(const BitVector* pBits);
+
+/* copy one vector to another of equal size */
+void dvmCopyBitVector(BitVector *dest, const BitVector *src);
+
+/*
+ * Intersect two bit vectors and store the result to the dest vector.
+ */
+bool dvmIntersectBitVectors(BitVector *dest, const BitVector *src1,
+ const BitVector *src2);
+
+/*
+ * Unify two bit vectors and store the result to the dest vector.
+ */
+bool dvmUnifyBitVectors(BitVector *dest, const BitVector *src1,
+ const BitVector *src2);
+
+/*
+ * Merge the contents of "src" into "dst", checking to see if this causes
+ * any changes to occur.
+ *
+ * Returns "true" if the contents of the destination vector were modified.
+ */
+bool dvmCheckMergeBitVectors(BitVector* dst, const BitVector* src);
+
+/*
+ * Compare two bit vectors and return true if difference is seen.
+ */
+bool dvmCompareBitVectors(const BitVector *src1, const BitVector *src2);
+
+/* Initialize the iterator structure */
+void dvmBitVectorIteratorInit(BitVector* pBits, BitVectorIterator* iterator);
+
+/* Return the next position set to 1. -1 means end-of-vector reached */
+int dvmBitVectorIteratorNext(BitVectorIterator* iterator);
+
+#endif /*_DALVIK_BITVECTOR*/
uLong adler = 0;
if (!modOkay) {
adler = adler32(0L, Z_NULL, 0);
- adler = adler32(adler, buf, len);
+ adler = adler32(adler, (const Bytef*)buf, len);
*(uLong*)newBuf = adler;
}
*/
if (!modOkay) {
uLong adler = adler32(0L, Z_NULL, 0);
- adler = adler32(adler, dataBuf, len);
+ adler = adler32(adler, (const Bytef*)dataBuf, len);
if (pExtra->adler != adler) {
LOGE("JNI: buffer modified (0x%08lx vs 0x%08lx) at addr %p",
pExtra->adler, adler, dataBuf);
CHECK_EXIT(env); \
return _retok; \
}
-CALL_VIRTUAL(jobject, Object, Object* result, result=, result, 'L');
+CALL_VIRTUAL(jobject, Object, Object* result, result=(Object*), result, 'L');
CALL_VIRTUAL(jboolean, Boolean, jboolean result, result=, result, 'Z');
CALL_VIRTUAL(jbyte, Byte, jbyte result, result=, result, 'B');
CALL_VIRTUAL(jchar, Char, jchar result, result=, result, 'C');
CHECK_EXIT(env); \
return _retok; \
}
-CALL_NONVIRTUAL(jobject, Object, Object* result, result=, result, 'L');
+CALL_NONVIRTUAL(jobject, Object, Object* result, result=(Object*), result, 'L');
CALL_NONVIRTUAL(jboolean, Boolean, jboolean result, result=, result, 'Z');
CALL_NONVIRTUAL(jbyte, Byte, jbyte result, result=, result, 'B');
CALL_NONVIRTUAL(jchar, Char, jchar result, result=, result, 'C');
CHECK_EXIT(env); \
return _retok; \
}
-CALL_STATIC(jobject, Object, Object* result, result=, result, 'L');
+CALL_STATIC(jobject, Object, Object* result, result=(Object*), result, 'L');
CALL_STATIC(jboolean, Boolean, jboolean result, result=, result, 'Z');
CALL_STATIC(jbyte, Byte, jbyte result, result=, result, 'B');
CALL_STATIC(jchar, Char, jchar result, result=, result, 'C');
result = BASE_ENV(env)->GetStringChars(env, string, isCopy);
if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {
// TODO: fix for indirect
- int len = dvmStringLen(string) * 2;
+ int len = dvmStringLen((StringObject*) string) * 2;
result = (const jchar*) createGuardedCopy(result, len, false);
if (isCopy != NULL)
*isCopy = JNI_TRUE;
result = BASE_ENV(env)->GetStringUTFChars(env, string, isCopy);
if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {
// TODO: fix for indirect
- int len = dvmStringUtf8ByteLen(string) + 1;
+ int len = dvmStringUtf8ByteLen((StringObject*) string) + 1;
result = (const char*) createGuardedCopy(result, len, false);
if (isCopy != NULL)
*isCopy = JNI_TRUE;
result = BASE_ENV(env)->GetStringCritical(env, string, isCopy);
if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {
// TODO: fix for indirect
- int len = dvmStringLen(string) * 2;
+ int len = dvmStringLen((StringObject*) string) * 2;
result = (const jchar*) createGuardedCopy(result, len, false);
if (isCopy != NULL)
*isCopy = JNI_TRUE;
#include "Inlines.h"
#include "Misc.h"
#include "Bits.h"
+#include "BitVector.h"
#include "libdex/SysUtil.h"
#include "libdex/DexFile.h"
#include "libdex/DexProto.h"
{
return (FrameId)(u4) frame;
}
-static void* frameIdToFrame(FrameId id)
+static u4* frameIdToFrame(FrameId id)
{
- return (void*)(u4) id;
+ return (u4*)(u4) id;
}
dvmHashTableLock(gDvm.loadedClasses);
*pNumClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
- pRefType = *pClassRefBuf = malloc(sizeof(RefTypeId) * *pNumClasses);
+ pRefType = *pClassRefBuf =
+ (RefTypeId*)malloc(sizeof(RefTypeId) * *pNumClasses);
if (dvmHashForeach(gDvm.loadedClasses, copyRefType, &pRefType) != 0) {
LOGW("Warning: problem getting class list\n");
/* over-allocate the return buffer */
maxClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
- *pClassRefBuf = malloc(sizeof(RefTypeId) * maxClasses);
+ *pClassRefBuf = (RefTypeId*)malloc(sizeof(RefTypeId) * maxClasses);
/*
* Run through the list, looking for matches.
framePtr = thread->curFrame;
while (framePtr != NULL) {
- if (!dvmIsBreakFrame(framePtr))
+ if (!dvmIsBreakFrame((u4*)framePtr))
count++;
framePtr = SAVEAREA_FROM_FP(framePtr)->prevFrame;
const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
const Method* method = saveArea->method;
- if (!dvmIsBreakFrame(framePtr)) {
+ if (!dvmIsBreakFrame((u4*)framePtr)) {
if (count == num) {
*pFrameId = frameToFrameId(framePtr);
if (dvmIsInterfaceClass(method->clazz))
}
/* need this for InstanceOnly filters */
- Object* thisObj = getThisObject(throwFp);
+ Object* thisObj = getThisObject((u4*)throwFp);
/*
* Hand the event to the JDWP exception handler. Note we're using the
*pResultTag = targetThread->invokeReq.resultTag;
if (isTagPrimitive(targetThread->invokeReq.resultTag))
*pResultValue = targetThread->invokeReq.resultValue.j;
- else
- *pResultValue = objectToObjectId(targetThread->invokeReq.resultValue.l);
+ else {
+ Object* tmpObj = (Object*)targetThread->invokeReq.resultValue.l;
+ *pResultValue = objectToObjectId(tmpObj);
+ }
*pExceptObj = targetThread->invokeReq.exceptObj;
err = targetThread->invokeReq.err;
pReq->resultValue.j = 0; /*0xadadadad;*/
} else if (pReq->resultTag == JT_OBJECT) {
/* if no exception thrown, examine object result more closely */
- u1 newTag = resultTagFromObject(pReq->resultValue.l);
+ u1 newTag = resultTagFromObject((Object*)pReq->resultValue.l);
if (newTag != pReq->resultTag) {
LOGVV(" JDWP promoted result from %d to %d\n",
pReq->resultTag, newTag);
* We can't use the "tracked allocation" mechanism here because
* the object is going to be handed off to a different thread.
*/
- (void) objectToObjectId(pReq->resultValue.l);
+ (void) objectToObjectId((Object*)pReq->resultValue.l);
}
if (oldExcept != NULL) {
u4 insnsSize = dvmGetMethodInsnsSize(method);
AddressSetContext context;
- result = calloc(1, sizeof(AddressSet) + (insnsSize/8) + 1);
+ result = (AddressSet*)calloc(1, sizeof(AddressSet) + (insnsSize/8) + 1);
result->setSize = insnsSize;
memset(&context, 0, sizeof(context));
#
LOCAL_CFLAGS += -fstrict-aliasing -Wstrict-aliasing=2 -fno-align-jumps
#LOCAL_CFLAGS += -DUSE_INDIRECT_REF
-LOCAL_CFLAGS += -Wall -Wextra -Wno-unused-parameter
+LOCAL_CFLAGS += -Wall -Wextra -Wno-unused-parameter -Wc++-compat
LOCAL_CFLAGS += -DARCH_VARIANT=\"$(dvm_arch_variant)\"
#
AllocTracker.c \
Atomic.c.arm \
AtomicCache.c \
+ BitVector.c.arm \
CheckJni.c \
Ddm.c \
Debugger.c \
Jni.c \
JarFile.c \
LinearAlloc.c \
- Misc.c.arm \
+ Misc.c \
Native.c \
PointerSet.c \
Profile.c \
analysis/CodeVerify.c \
analysis/DexPrepare.c \
analysis/DexVerify.c \
+ analysis/Liveness.c \
analysis/Optimize.c \
analysis/RegisterMap.c \
analysis/VerifySubs.c \
+ analysis/VfyBasicBlock.c \
hprof/Hprof.c \
hprof/HprofClass.c \
hprof/HprofHeap.c \
compiler/InlineTransformation.c \
compiler/IntermediateRep.c \
compiler/Dataflow.c \
+ compiler/MethodSSATransformation.c \
compiler/Loop.c \
compiler/Ralloc.c \
interp/Jit.c
goto bail;
}
- pDexFile = dexFileParse(memMap.addr, memMap.length, parseFlags);
+ pDexFile = dexFileParse((u1*)memMap.addr, memMap.length, parseFlags);
if (pDexFile == NULL) {
LOGE("DEX parse failed\n");
sysReleaseShmem(&memMap);
parseFlags |= kDexParseVerifyChecksum;
*/
- pDexFile = dexFileParse(addr, len, parseFlags);
+ pDexFile = dexFileParse((u1*)addr, len, parseFlags);
if (pDexFile == NULL) {
LOGE("DEX parse failed\n");
goto bail;
* if this was a native method.
*/
assert(saveArea->prevFrame != NULL);
- if (dvmIsBreakFrame(saveArea->prevFrame)) {
+ if (dvmIsBreakFrame((u4*)saveArea->prevFrame)) {
if (!scanOnly)
break; // bail with catchAddr == -1
saveArea = SAVEAREA_FROM_FP(fp);
fp = saveArea->prevFrame; // this may be a good one
while (fp != NULL) {
- if (!dvmIsBreakFrame(fp)) {
+ if (!dvmIsBreakFrame((u4*)fp)) {
saveArea = SAVEAREA_FROM_FP(fp);
if (!dvmIsNativeMethod(saveArea->method))
break;
const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
const Method* method = saveArea->method;
- if (dvmIsBreakFrame(fp))
+ if (dvmIsBreakFrame((u4*)fp))
break;
if (!dvmInstanceof(method->clazz, gDvm.classJavaLangThrowable))
break;
while (fp != NULL) {
const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
- if (!dvmIsBreakFrame(fp))
+ if (!dvmIsBreakFrame((u4*)fp))
stackDepth++;
assert(fp != saveArea->prevFrame);
const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
const Method* method = saveArea->method;
- if (!dvmIsBreakFrame(fp)) {
+ if (!dvmIsBreakFrame((u4*)fp)) {
//LOGD("EXCEP keeping %s.%s\n", method->clazz->descriptor,
// method->name);
} ExecutionMode;
/*
+ * Register map generation mode. Only applicable when generateRegisterMaps
+ * is enabled. (The "disabled" state is not folded into this because
+ * there are callers like dexopt that want to enable/disable without
+ * specifying the configuration details.)
+ *
+ * "TypePrecise" is slower and requires additional storage for the register
+ * maps, but allows type-precise GC. "LivePrecise" is even slower and
+ * requires additional heap during processing, but allows live-precise GC.
+ */
+typedef enum {
+ kRegisterMapModeUnknown = 0,
+ kRegisterMapModeTypePrecise,
+ kRegisterMapModeLivePrecise
+} RegisterMapMode;
+
+/*
* All fields are initialized to zero.
*
* Storage allocated here must be freed by a subsystem shutdown function or
DexOptimizerMode dexOptMode;
DexClassVerifyMode classVerifyMode;
+ bool generateRegisterMaps;
+ RegisterMapMode registerMapMode;
+
bool monitorVerification;
bool dexOptForSmp;
bool preciseGc;
bool preVerify;
bool postVerify;
- bool generateRegisterMaps;
bool concurrentMarkSweep;
bool verifyCardTable;
#if defined(WITH_JIT)
+/* Trace profiling modes. Ordering matters - off states before on states */
+typedef enum TraceProfilingModes {
+ kTraceProfilingDisabled = 0, // Not profiling
+ kTraceProfilingPeriodicOff = 1, // Periodic profiling, off phase
+ kTraceProfilingContinuous = 2, // Always profiling
+ kTraceProfilingPeriodicOn = 3 // Periodic profiling, on phase
+} TraceProfilingModes;
+
/*
* Exiting the compiled code w/o chaining will incur overhead to look up the
* target in the code cache which is extra work only when JIT is enabled. So
* are stored in each thread. */
struct JitEntry *pJitEntryTable;
- /* Array of profile threshold counters */
+ /* Array of compilation trigger threshold counters */
unsigned char *pProfTable;
+ /* Trace profiling counters */
+ struct JitTraceProfCounters *pJitTraceProfCounters;
+
/* Copy of pProfTable used for temporarily disabling the Jit */
unsigned char *pProfTableCopy;
/* Flag to dump all compiled code */
bool printMe;
- /* Flag to count trace execution */
- bool profile;
+ /* Trace profiling mode */
+ TraceProfilingModes profileMode;
+
+ /* Periodic trace profiling countdown timer */
+ int profileCountdown;
/* Vector to disable selected optimizations */
int disableOpt;
updateSlotRemove(pRef, idx);
#ifndef NDEBUG
- pRef->table[idx] = (IndirectRef) 0xd3d3d3d3;
+ pRef->table[idx] = (Object*)0xd3d3d3d3;
#endif
int numHoles =
*/
INLINE IndirectRefKind dvmGetIndirectRefType(IndirectRef iref)
{
- return (u4) iref & 0x03;
+ return (IndirectRefKind)((u4) iref & 0x03);
}
/*
dvmFprintf(stderr, " -Xgc:[no]postverify\n");
dvmFprintf(stderr, " -Xgc:[no]concurrent\n");
dvmFprintf(stderr, " -Xgc:[no]verifycardtable\n");
- dvmFprintf(stderr, " -Xgenregmap\n");
+ dvmFprintf(stderr, " -X[no]genregmap\n");
dvmFprintf(stderr, " -Xverifyopt:[no]checkmon\n");
dvmFprintf(stderr, " -Xcheckdexsum\n");
#if defined(WITH_JIT)
} else if (strncmp(argv[i], "-Xjitverbose", 12) == 0) {
gDvmJit.printMe = true;
} else if (strncmp(argv[i], "-Xjitprofile", 12) == 0) {
- gDvmJit.profile = true;
+ gDvmJit.profileMode = kTraceProfilingContinuous;
} else if (strncmp(argv[i], "-Xjitdisableopt", 15) == 0) {
/* Disable selected optimizations */
if (argv[i][15] == ':') {
} else if (strcmp(argv[i], "-Xgenregmap") == 0) {
gDvm.generateRegisterMaps = true;
- LOGV("Register maps will be generated during verification\n");
+ } else if (strcmp(argv[i], "-Xnogenregmap") == 0) {
+ gDvm.generateRegisterMaps = false;
} else if (strcmp(argv[i], "Xverifyopt:checkmon") == 0) {
gDvm.monitorVerification = true;
gDvm.classVerifyMode = VERIFY_MODE_ALL;
gDvm.dexOptMode = OPTIMIZE_MODE_VERIFIED;
gDvm.monitorVerification = false;
+ gDvm.generateRegisterMaps = true;
+ gDvm.registerMapMode = kRegisterMapModeTypePrecise;
/*
* Default execution mode.
/*
* Check the literal table for a match.
*/
- StringObject* literal = dvmHashTableLookup(gDvm.literalStrings,
+ StringObject* literal = (StringObject*)dvmHashTableLookup(gDvm.literalStrings,
hash, strObj,
dvmHashcmpStrings,
false);
* There is no match in the literal table, check the
* interned string table.
*/
- StringObject* interned = dvmHashTableLookup(gDvm.internedStrings,
+ StringObject* interned = (StringObject*)dvmHashTableLookup(gDvm.internedStrings,
hash, strObj,
dvmHashcmpStrings,
false);
* matching string to the literal table.
*/
dvmHashTableRemove(gDvm.internedStrings, hash, interned);
- found = dvmHashTableLookup(gDvm.literalStrings,
+ found = (StringObject*)dvmHashTableLookup(gDvm.literalStrings,
hash, interned,
dvmHashcmpStrings,
true);
* No match in the literal table or the interned
* table. Insert into the literal table.
*/
- found = dvmHashTableLookup(gDvm.literalStrings,
+ found = (StringObject*)dvmHashTableLookup(gDvm.literalStrings,
hash, strObj,
dvmHashcmpStrings,
true);
/*
* Check the literal table for a match.
*/
- found = dvmHashTableLookup(gDvm.literalStrings,
+ found = (StringObject*)dvmHashTableLookup(gDvm.literalStrings,
hash, strObj,
dvmHashcmpStrings,
false);
* No match was found in the literal table. Insert into
* the intern table.
*/
- found = dvmHashTableLookup(gDvm.internedStrings,
+ found = (StringObject*)dvmHashTableLookup(gDvm.internedStrings,
hash, strObj,
dvmHashcmpStrings,
true);
}
dvmLockMutex(&gDvm.internLock);
hash = dvmComputeStringHash(strObj);
- found = dvmHashTableLookup(gDvm.internedStrings, hash, (void*)strObj,
- dvmHashcmpStrings, false);
+ found = (StringObject*)dvmHashTableLookup(gDvm.internedStrings, hash,
+ (StringObject*)strObj, dvmHashcmpStrings, false);
dvmUnlockMutex(&gDvm.internLock);
return found == strObj;
}
size_t bufLen = fileNameLen + suffixLen + 1;
int fd = -1;
- buf = malloc(bufLen);
+ buf = (char*)malloc(bufLen);
if (buf == NULL) {
errno = ENOMEM;
return -1;
}
#else
if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable,
- gDvm.jniGlobalRefTable.table, jobj))
+ gDvm.jniGlobalRefTable.table, (Object*)jobj))
{
LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n",
jobj, dvmIsValidObject((Object*) jobj));
u4* fp;
int i;
- fp = self->curFrame;
+ fp = (u4*)self->curFrame;
while (1) {
/*
* Back up over JNI PushLocalFrame frames. This works because the
meth = saveArea->method;
if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method)
break;
- fp = saveArea->prevFrame;
+ fp = (u4*)saveArea->prevFrame;
}
LOGVV("+++ scanning %d args in %s (%s)\n",
return (jobjectRefType) dvmGetIndirectRefType(jobj);
}
#else
+ Object* obj = dvmDecodeIndirectRef(env, jobj);
ReferenceTable* pRefTable = getLocalRefTable(env);
Thread* self = dvmThreadSelf();
}
/* check args */
- if (findInArgList(self, jobj)) {
- //LOGI("--- REF found %p on stack\n", jobj);
+ if (findInArgList(self, obj)) {
+ //LOGI("--- REF found %p on stack", obj);
return JNILocalRefType;
}
/* check locals */
- if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) {
- //LOGI("--- REF found %p in locals\n", jobj);
+ if (dvmFindInReferenceTable(pRefTable, pRefTable->table,
+ obj) != NULL) {
+ //LOGI("--- REF found %p in locals", obj);
return JNILocalRefType;
}
/* check globals */
dvmLockMutex(&gDvm.jniGlobalRefLock);
if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable,
- gDvm.jniGlobalRefTable.table, jobj))
+ gDvm.jniGlobalRefTable.table, obj))
{
- //LOGI("--- REF found %p in globals\n", jobj);
+ //LOGI("--- REF found %p in globals", obj);
dvmUnlockMutex(&gDvm.jniGlobalRefLock);
return JNIGlobalRefType;
}
DalvikBridgeFunc bridge = shouldTrace(method)
? dvmTraceCallJNIMethod
: dvmSelectJNIBridge(method);
- dvmSetNativeFunc(method, bridge, func);
+ dvmSetNativeFunc(method, bridge, (const u2*)func);
}
/*
assert(method->insns != NULL);
COMPUTE_STACK_SUM(self);
- dvmPlatformInvoke(env, staticMethodClass,
+ dvmPlatformInvoke(env, (ClassObject*)staticMethodClass,
method->jniArgInfo, method->insSize, modArgs, method->shorty,
(void*)method->insns, pResult);
CHECK_STACK_SUM(self);
ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */
COMPUTE_STACK_SUM(self);
- dvmPlatformInvoke(self->jniEnv, staticMethodClass,
+ dvmPlatformInvoke(self->jniEnv, (ClassObject*)staticMethodClass,
method->jniArgInfo, method->insSize, args, method->shorty,
(void*)method->insns, pResult);
CHECK_STACK_SUM(self);
/*
* Set a static field.
*/
-#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
+#define SET_STATIC_TYPE_FIELD(_ctype, _ctype2, _jname, _isref) \
static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
jfieldID fieldID, _ctype value) \
{ \
dvmDecodeIndirectRef(env, (jobject)(u4)value); \
dvmSetStaticFieldObjectVolatile(sfield, valObj); \
} else { \
- dvmSetStaticField##_jname##Volatile(sfield, value); \
+ dvmSetStaticField##_jname##Volatile(sfield, (_ctype2)value);\
} \
} else { \
if (_isref) { \
dvmDecodeIndirectRef(env, (jobject)(u4)value); \
dvmSetStaticFieldObject(sfield, valObj); \
} else { \
- dvmSetStaticField##_jname(sfield, value); \
+ dvmSetStaticField##_jname(sfield, (_ctype2)value); \
} \
} \
JNI_EXIT(); \
}
-SET_STATIC_TYPE_FIELD(jobject, Object, true);
-SET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
-SET_STATIC_TYPE_FIELD(jbyte, Byte, false);
-SET_STATIC_TYPE_FIELD(jchar, Char, false);
-SET_STATIC_TYPE_FIELD(jshort, Short, false);
-SET_STATIC_TYPE_FIELD(jint, Int, false);
-SET_STATIC_TYPE_FIELD(jlong, Long, false);
-SET_STATIC_TYPE_FIELD(jfloat, Float, false);
-SET_STATIC_TYPE_FIELD(jdouble, Double, false);
+SET_STATIC_TYPE_FIELD(jobject, Object*, Object, true);
+SET_STATIC_TYPE_FIELD(jboolean, bool, Boolean, false);
+SET_STATIC_TYPE_FIELD(jbyte, s1, Byte, false);
+SET_STATIC_TYPE_FIELD(jchar, u2, Char, false);
+SET_STATIC_TYPE_FIELD(jshort, s2, Short, false);
+SET_STATIC_TYPE_FIELD(jint, s4, Int, false);
+SET_STATIC_TYPE_FIELD(jlong, s8, Long, false);
+SET_STATIC_TYPE_FIELD(jfloat, float, Float, false);
+SET_STATIC_TYPE_FIELD(jdouble, double, Double, false);
/*
* Get an instance field.
/*
* Set an instance field.
*/
-#define SET_TYPE_FIELD(_ctype, _jname, _isref) \
+#define SET_TYPE_FIELD(_ctype, _ctype2, _jname, _isref) \
static void Set##_jname##Field(JNIEnv* env, jobject jobj, \
jfieldID fieldID, _ctype value) \
{ \
dvmSetFieldObjectVolatile(obj, field->byteOffset, valObj); \
} else { \
dvmSetField##_jname##Volatile(obj, \
- field->byteOffset, value); \
+ field->byteOffset, (_ctype2)value); \
} \
} else { \
if (_isref) { \
dvmDecodeIndirectRef(env, (jobject)(u4)value); \
dvmSetFieldObject(obj, field->byteOffset, valObj); \
} else { \
- dvmSetField##_jname(obj, field->byteOffset, value); \
+ dvmSetField##_jname(obj, \
+ field->byteOffset, (_ctype2)value); \
} \
} \
JNI_EXIT(); \
}
-SET_TYPE_FIELD(jobject, Object, true);
-SET_TYPE_FIELD(jboolean, Boolean, false);
-SET_TYPE_FIELD(jbyte, Byte, false);
-SET_TYPE_FIELD(jchar, Char, false);
-SET_TYPE_FIELD(jshort, Short, false);
-SET_TYPE_FIELD(jint, Int, false);
-SET_TYPE_FIELD(jlong, Long, false);
-SET_TYPE_FIELD(jfloat, Float, false);
-SET_TYPE_FIELD(jdouble, Double, false);
+SET_TYPE_FIELD(jobject, Object*, Object, true);
+SET_TYPE_FIELD(jboolean, bool, Boolean, false);
+SET_TYPE_FIELD(jbyte, s1, Byte, false);
+SET_TYPE_FIELD(jchar, u2, Char, false);
+SET_TYPE_FIELD(jshort, s2, Short, false);
+SET_TYPE_FIELD(jint, s4, Int, false);
+SET_TYPE_FIELD(jlong, s8, Long, false);
+SET_TYPE_FIELD(jfloat, float, Float, false);
+SET_TYPE_FIELD(jdouble, double, Double, false);
/*
* Make a virtual method call.
dvmCallMethodV(_self, meth, obj, true, &result, args); \
va_end(args); \
if (_isref && !dvmCheckException(_self)) \
- result.l = addLocalReference(env, result.l); \
+ result.l = addLocalReference(env, (Object*)result.l); \
JNI_EXIT(); \
return _retok; \
} \
} \
dvmCallMethodV(_self, meth, obj, true, &result, args); \
if (_isref && !dvmCheckException(_self)) \
- result.l = addLocalReference(env, result.l); \
+ result.l = addLocalReference(env, (Object*)result.l); \
JNI_EXIT(); \
return _retok; \
} \
} \
dvmCallMethodA(_self, meth, obj, true, &result, args); \
if (_isref && !dvmCheckException(_self)) \
- result.l = addLocalReference(env, result.l); \
+ result.l = addLocalReference(env, (Object*)result.l); \
JNI_EXIT(); \
return _retok; \
}
va_start(args, methodID); \
dvmCallMethodV(_self, meth, obj, true, &result, args); \
if (_isref && !dvmCheckException(_self)) \
- result.l = addLocalReference(env, result.l); \
+ result.l = addLocalReference(env, (Object*)result.l); \
va_end(args); \
JNI_EXIT(); \
return _retok; \
} \
dvmCallMethodV(_self, meth, obj, true, &result, args); \
if (_isref && !dvmCheckException(_self)) \
- result.l = addLocalReference(env, result.l); \
+ result.l = addLocalReference(env, (Object*)result.l); \
JNI_EXIT(); \
return _retok; \
} \
} \
dvmCallMethodA(_self, meth, obj, true, &result, args); \
if (_isref && !dvmCheckException(_self)) \
- result.l = addLocalReference(env, result.l); \
+ result.l = addLocalReference(env, (Object*)result.l); \
JNI_EXIT(); \
return _retok; \
}
dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
va_end(args); \
if (_isref && !dvmCheckException(_self)) \
- result.l = addLocalReference(env, result.l); \
+ result.l = addLocalReference(env, (Object*)result.l); \
JNI_EXIT(); \
return _retok; \
} \
JValue result; \
dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
if (_isref && !dvmCheckException(_self)) \
- result.l = addLocalReference(env, result.l); \
+ result.l = addLocalReference(env, (Object*)result.l); \
JNI_EXIT(); \
return _retok; \
} \
JValue result; \
dvmCallMethodA(_self, (Method*)methodID, NULL, true, &result, args);\
if (_isref && !dvmCheckException(_self)) \
- result.l = addLocalReference(env, result.l); \
+ result.l = addLocalReference(env, (Object*)result.l); \
JNI_EXIT(); \
return _retok; \
}
fprintf(stderr, "ERROR: arg %d string was null\n", i);
goto bail;
} else if (strcmp(optStr, "vfprintf") == 0) {
- gDvm.vfprintfHook = args->options[i].extraInfo;
+ gDvm.vfprintfHook =
+ (int (*)(FILE *, const char*, va_list))args->options[i].extraInfo;
} else if (strcmp(optStr, "exit") == 0) {
- gDvm.exitHook = args->options[i].extraInfo;
+ gDvm.exitHook = (void (*)(int)) args->options[i].extraInfo;
} else if (strcmp(optStr, "abort") == 0) {
- gDvm.abortHook = args->options[i].extraInfo;
+ gDvm.abortHook = (void (*)(void))args->options[i].extraInfo;
} else if (strcmp(optStr, "sensitiveThread") == 0) {
- gDvm.isSensitiveThreadHook = args->options[i].extraInfo;
+ gDvm.isSensitiveThreadHook = (bool (*)(void))args->options[i].extraInfo;
} else if (strcmp(optStr, "-Xcheck:jni") == 0) {
checkJni = true;
} else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
return NULL;
}
- pHdr->mapAddr = mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE,
+ pHdr->mapAddr = (char*)mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE,
MAP_PRIVATE, fd, 0);
if (pHdr->mapAddr == MAP_FAILED) {
LOGE("LinearAlloc mmap(%d) failed: %s\n", pHdr->mapLength,
if (ENFORCE_READ_ONLY) {
/* allocate the per-page ref count */
int numPages = (pHdr->mapLength+SYSTEM_PAGE_SIZE-1) / SYSTEM_PAGE_SIZE;
- pHdr->writeRefCount = calloc(numPages, sizeof(short));
+ pHdr->writeRefCount = (short*)calloc(numPages, sizeof(short));
if (pHdr->writeRefCount == NULL) {
free(pHdr);
return NULL;
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
/*
* Miscellaneous utility functions.
*/
size_t length, HexDumpMode mode)
{
static const char gHexDigit[] = "0123456789abcdef";
- const unsigned char* addr = vaddr;
+ const unsigned char* addr = (const unsigned char*)vaddr;
char out[77]; /* exact fit */
unsigned int offset; /* offset to show while printing */
char* hex;
/*
- * Allocate a bit vector with enough space to hold at least the specified
- * number of bits.
- */
-BitVector* dvmAllocBitVector(int startBits, bool expandable)
-{
- BitVector* bv;
- int count;
-
- assert(sizeof(bv->storage[0]) == 4); /* assuming 32-bit units */
- assert(startBits >= 0);
-
- bv = (BitVector*) malloc(sizeof(BitVector));
-
- count = (startBits + 31) >> 5;
-
- bv->storageSize = count;
- bv->expandable = expandable;
- bv->storage = (u4*) malloc(count * sizeof(u4));
- memset(bv->storage, 0x00, count * sizeof(u4));
- return bv;
-}
-
-/*
- * Free a BitVector.
- */
-void dvmFreeBitVector(BitVector* pBits)
-{
- if (pBits == NULL)
- return;
-
- free(pBits->storage);
- free(pBits);
-}
-
-/*
- * "Allocate" the first-available bit in the bitmap.
- *
- * This is not synchronized. The caller is expected to hold some sort of
- * lock that prevents multiple threads from executing simultaneously in
- * dvmAllocBit/dvmFreeBit.
- */
-int dvmAllocBit(BitVector* pBits)
-{
- int word, bit;
-
-retry:
- for (word = 0; word < pBits->storageSize; word++) {
- if (pBits->storage[word] != 0xffffffff) {
- /*
- * There are unallocated bits in this word. Return the first.
- */
- bit = ffs(~(pBits->storage[word])) -1;
- assert(bit >= 0 && bit < 32);
- pBits->storage[word] |= 1 << bit;
- return (word << 5) | bit;
- }
- }
-
- /*
- * Ran out of space, allocate more if we're allowed to.
- */
- if (!pBits->expandable)
- return -1;
-
- pBits->storage = realloc(pBits->storage,
- (pBits->storageSize + kBitVectorGrowth) * sizeof(u4));
- memset(&pBits->storage[pBits->storageSize], 0x00,
- kBitVectorGrowth * sizeof(u4));
- pBits->storageSize += kBitVectorGrowth;
- goto retry;
-}
-
-/*
- * Mark the specified bit as "set".
- *
- * Returns "false" if the bit is outside the range of the vector and we're
- * not allowed to expand.
- */
-bool dvmSetBit(BitVector* pBits, int num)
-{
- assert(num >= 0);
- if (num >= pBits->storageSize * (int)sizeof(u4) * 8) {
- if (!pBits->expandable)
- return false;
-
- /* Round up to word boundaries for "num+1" bits */
- int newSize = (num + 1 + 31) >> 5;
- assert(newSize > pBits->storageSize);
- pBits->storage = realloc(pBits->storage, newSize * sizeof(u4));
- memset(&pBits->storage[pBits->storageSize], 0x00,
- (newSize - pBits->storageSize) * sizeof(u4));
- pBits->storageSize = newSize;
- }
-
- pBits->storage[num >> 5] |= 1 << (num & 0x1f);
- return true;
-}
-
-/*
- * Mark the specified bit as "clear".
- */
-void dvmClearBit(BitVector* pBits, int num)
-{
- assert(num >= 0 && num < (int) pBits->storageSize * (int)sizeof(u4) * 8);
-
- pBits->storage[num >> 5] &= ~(1 << (num & 0x1f));
-}
-
-/*
- * Mark all bits bit as "clear".
- */
-void dvmClearAllBits(BitVector* pBits)
-{
- int count = pBits->storageSize;
- memset(pBits->storage, 0, count * sizeof(u4));
-}
-
-/*
- * Determine whether or not the specified bit is set.
- */
-bool dvmIsBitSet(const BitVector* pBits, int num)
-{
- assert(num >= 0 && num < (int) pBits->storageSize * (int)sizeof(u4) * 8);
-
- int val = pBits->storage[num >> 5] & (1 << (num & 0x1f));
- return (val != 0);
-}
-
-/*
- * Count the number of bits that are set.
- */
-int dvmCountSetBits(const BitVector* pBits)
-{
- int word;
- int count = 0;
-
- for (word = 0; word < pBits->storageSize; word++) {
- u4 val = pBits->storage[word];
-
- if (val != 0) {
- if (val == 0xffffffff) {
- count += 32;
- } else {
- /* count the number of '1' bits */
- while (val != 0) {
- val &= val - 1;
- count++;
- }
- }
- }
- }
-
- return count;
-}
-
-/*
- * Copy a whole vector to the other. Only do that when the both vectors have
- * the same size and attribute.
- */
-bool dvmCopyBitVector(BitVector *dest, const BitVector *src)
-{
- if (dest->storageSize != src->storageSize ||
- dest->expandable != src->expandable)
- return false;
- memcpy(dest->storage, src->storage, sizeof(u4) * dest->storageSize);
- return true;
-}
-
-/*
- * Intersect two bit vectores and merge the result on top of the pre-existing
- * value in the dest vector.
- */
-bool dvmIntersectBitVectors(BitVector *dest, const BitVector *src1,
- const BitVector *src2)
-{
- if (dest->storageSize != src1->storageSize ||
- dest->storageSize != src2->storageSize ||
- dest->expandable != src1->expandable ||
- dest->expandable != src2->expandable)
- return false;
-
- int i;
- for (i = 0; i < dest->storageSize; i++) {
- dest->storage[i] |= src1->storage[i] & src2->storage[i];
- }
- return true;
-}
-
-/*
* Return a newly-allocated string in which all occurrences of '.' have
* been changed to '/'. If we find a '/' in the original string, NULL
* is returned to avoid ambiguity.
}
// Allocate enough space.
- char* result = malloc(resultLength + 1);
+ char* result = (char*)malloc(resultLength + 1);
if (result == NULL) {
return NULL;
}
str++; /* Skip the 'L'. */
}
- newStr = malloc(at + 1); /* Add one for the '\0'. */
+ newStr = (char*)malloc(at + 1); /* Add one for the '\0'. */
if (newStr == NULL)
return NULL;
wrapElSemi = 1;
}
- newStr = at = malloc(length + 1); /* + 1 for the '\0' */
+ newStr = at = (char*)malloc(length + 1); /* + 1 for the '\0' */
if (newStr == NULL) {
return NULL;
{
if (str[0] == 'L') {
size_t length = strlen(str) - 1;
- char* newStr = malloc(length);
+ char* newStr = (char*)malloc(length);
if (newStr == NULL) {
return NULL;
{
if (str[0] != '[') {
size_t length = strlen(str);
- char* descriptor = malloc(length + 3);
+ char* descriptor = (char*)malloc(length + 3);
if (descriptor == NULL) {
return NULL;
#endif
;
-
-/*
- * Expanding bitmap, used for tracking resources. Bits are numbered starting
- * from zero.
- *
- * All operations on a BitVector are unsynchronized.
- */
-typedef struct BitVector {
- bool expandable; /* expand bitmap if we run out? */
- int storageSize; /* current size, in 32-bit words */
- u4* storage;
-} BitVector;
-
-/* allocate a bit vector with enough space to hold "startBits" bits */
-BitVector* dvmAllocBitVector(int startBits, bool expandable);
-void dvmFreeBitVector(BitVector* pBits);
-
-/*
- * dvmAllocBit always allocates the first possible bit. If we run out of
- * space in the bitmap, and it's not marked expandable, dvmAllocBit
- * returns -1.
- *
- * dvmSetBit sets the specified bit, expanding the vector if necessary
- * (and possible).
- *
- * dvmIsBitSet returns "true" if the bit is set.
- */
-int dvmAllocBit(BitVector* pBits);
-bool dvmSetBit(BitVector* pBits, int num);
-void dvmClearBit(BitVector* pBits, int num);
-void dvmClearAllBits(BitVector* pBits);
-bool dvmIsBitSet(const BitVector* pBits, int num);
-
-/* count the number of bits that have been set */
-int dvmCountSetBits(const BitVector* pBits);
-
-/* copy one vector to the other compatible one */
-bool dvmCopyBitVector(BitVector *dest, const BitVector *src);
-
-/*
- * Intersect two bit vectores and merge the result on top of the pre-existing
- * value in the dest vector.
- */
-bool dvmIntersectBitVectors(BitVector *dest, const BitVector *src1,
- const BitVector *src2);
-
-#define kBitVectorGrowth 4 /* increase by 4 u4s when limit hit */
-
-
/*
* Return a newly-allocated string in which all occurrences of '.' have
* been changed to '/'. If we find a '/' in the original string, NULL
ent = dvmHashTableLookup(gDvm.nativeLibs, hash, (void*)pathName,
hashcmpNameStr, false);
- return ent;
+ return (SharedLib*)ent;
}
/*
* our own pointer back. If somebody beat us to the punch, we'll get
* their pointer back instead.
*/
- return dvmHashTableLookup(gDvm.nativeLibs, hash, pLib, hashcmpSharedLib,
- true);
+ return (SharedLib*)dvmHashTableLookup(gDvm.nativeLibs, hash, pLib,
+ hashcmpSharedLib, true);
}
/*
* top of the stack is around Runtime.loadLibrary(). (See
* the comments in the JNI FindClass function.)
*/
- OnLoadFunc func = vonLoad;
+ OnLoadFunc func = (OnLoadFunc)vonLoad;
Object* prevOverride = self->classLoaderOverride;
self->classLoaderOverride = classLoader;
*pLen = 4 + descriptorLength + strlen(methodName);
- result = malloc(*pLen +1);
+ result = (char*)malloc(*pLen +1);
if (result == NULL)
return NULL;
*/
PointerSet* dvmPointerSetAlloc(int initialSize)
{
- PointerSet* pSet = calloc(1, sizeof(PointerSet));
+ PointerSet* pSet = (PointerSet*)calloc(1, sizeof(PointerSet));
if (pSet != NULL) {
if (initialSize > 0) {
- pSet->list = malloc(sizeof(const void*) * initialSize);
+ pSet->list = (const void**)malloc(sizeof(void*) * initialSize);
if (pSet->list == NULL) {
free(pSet);
return NULL;
else
pSet->alloc *= 2;
LOGVV("expanding %p to %d\n", pSet, pSet->alloc);
- newList = realloc(pSet->list, pSet->alloc * sizeof(const void*));
+ newList = (const void**)realloc(pSet->list, pSet->alloc * sizeof(void*));
if (newList == NULL) {
LOGE("Failed expanding ptr set (alloc=%d)\n", pSet->alloc);
dvmAbort();
*/
void dvmPointerSetDump(const PointerSet* pSet)
{
+ LOGI("PointerSet %p\n", pSet);
int i;
for (i = 0; i < pSet->count; i++)
- printf(" %p", pSet->list[i]);
+ LOGI(" %2d: %p", i, pSet->list[i]);
}
#include <unistd.h>
#include <pthread.h>
#include <time.h>
-#include <sys/time.h>
#include <errno.h>
#define LOG_THIN LOGV
{
volatile u4 *thinp;
ThreadStatus oldStatus;
- useconds_t sleepDelay;
- const useconds_t maxSleepDelay = 1 << 20;
+ struct timespec tm;
+ long sleepDelayNs;
+ long minSleepDelayNs = 1000000; /* 1 millisecond */
+ long maxSleepDelayNs = 1000000000; /* 1 second */
u4 thin, newThin, threadId;
assert(self != NULL);
/*
* Spin until the thin lock is released or inflated.
*/
- sleepDelay = 0;
+ sleepDelayNs = 0;
for (;;) {
thin = *thinp;
/*
* The lock has not been released. Yield so
* the owning thread can run.
*/
- if (sleepDelay == 0) {
+ if (sleepDelayNs == 0) {
sched_yield();
- sleepDelay = 1000;
+ sleepDelayNs = minSleepDelayNs;
} else {
- usleep(sleepDelay);
- if (sleepDelay < maxSleepDelay / 2) {
- sleepDelay *= 2;
+ tm.tv_sec = 0;
+ tm.tv_nsec = sleepDelayNs;
+ nanosleep(&tm, NULL);
+ /*
+ * Prepare the next delay value. Wrap to
+ * avoid once a second polls for eternity.
+ */
+ if (sleepDelayNs < maxSleepDelayNs / 2) {
+ sleepDelayNs *= 2;
+ } else {
+ sleepDelayNs = minSleepDelayNs;
}
}
}
}
memset(stackBottom, 0xc5, interpStackSize); // stop valgrind complaints
#else
- stackBottom = mmap(NULL, interpStackSize, PROT_READ | PROT_WRITE,
+ stackBottom = (u1*) mmap(NULL, interpStackSize, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, -1, 0);
if (stackBottom == MAP_FAILED) {
#if defined(WITH_SELF_VERIFICATION)
if (curDepth == 1) {
/* not expecting a lingering break frame; just look at curFrame */
- assert(!dvmIsBreakFrame(self->curFrame));
+ assert(!dvmIsBreakFrame((u4*)self->curFrame));
StackSaveArea* ssa = SAVEAREA_FROM_FP(self->curFrame);
if (dvmIsNativeMethod(ssa->method))
topIsNative = true;
}
/* allocate on GC heap; memory is zeroed out */
- newObj = dvmMalloc(clazz->objectSize, flags);
+ newObj = (Object*)dvmMalloc(clazz->objectSize, flags);
if (newObj != NULL) {
DVM_OBJECT_INIT(newObj, clazz);
#if WITH_HPROF_STACK
size = obj->clazz->objectSize;
}
- copy = dvmMalloc(size, flags);
+ copy = (Object*)dvmMalloc(size, flags);
if (copy == NULL)
return NULL;
#if WITH_HPROF_STACK
static void countInstancesOfClassCallback(void *ptr, void *arg)
{
- CountContext *ctx = arg;
- const Object *obj = ptr;
+ CountContext *ctx = (CountContext *)arg;
+ const Object *obj = (const Object *)ptr;
assert(ctx != NULL);
if (obj->clazz == ctx->clazz) {
static void countAssignableInstancesOfClassCallback(void *ptr, void *arg)
{
- CountContext *ctx = arg;
- const Object *obj = ptr;
+ CountContext *ctx = (CountContext *)arg;
+ const Object *obj = (const Object *)ptr;
assert(ctx != NULL);
if (dvmInstanceof(obj->clazz, ctx->clazz)) {
if (allocBase == NULL) {
return false;
}
- gcHeap->cardTableBase = allocBase;
+ gcHeap->cardTableBase = (u1*)allocBase;
gcHeap->cardTableLength = length;
/* All zeros is the correct initial value; all clean. */
assert(GC_CARD_CLEAN == 0);
return;
}
assert(dvmIsValidObject(obj));
- ctx = arg;
+ ctx = (WhiteReferenceCounter *)arg;
if (dvmHeapBitmapIsObjectBitSet(ctx->markBits, obj)) {
return;
}
return;
}
assert(dvmIsValidObject(obj));
- ctx = arg;
+ ctx = (WhiteReferenceCounter*)arg;
if (dvmHeapBitmapIsObjectBitSet(ctx->markBits, obj)) {
return;
}
static void dumpReferencesCallback(void *ptr, void *arg)
{
- Object *obj = arg;
+ Object *obj = (Object *)arg;
if (ptr == obj) {
return;
}
- dvmVisitObject(dumpReferencesVisitor, ptr, &obj);
+ dvmVisitObject(dumpReferencesVisitor, (Object *)ptr, &obj);
if (obj == NULL) {
LOGD("Found %p in the heap @ %p", arg, ptr);
- dvmDumpObject(ptr);
+ dvmDumpObject((Object *)ptr);
}
}
*/
static void verifyCardTableCallback(void *ptr, void *arg)
{
- Object *obj = ptr;
- WhiteReferenceCounter ctx = { arg, 0 };
+ Object *obj = (Object *)ptr;
+ WhiteReferenceCounter ctx = { (HeapBitmap *)arg, 0 };
dvmVisitObject(countWhiteReferenceVisitor, obj, &ctx);
if (ctx.whiteRefs == 0) {
*/
state = HPSG_STATE(SOLIDITY_FREE, 0);
} else {
- const Object *obj = userptr;
+ const Object *obj = (const Object *)userptr;
/* If we're looking at the native heap, we'll just return
* (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks
*/
* internal tracking list.
*/
if ((flags & ALLOC_DONT_TRACK) == 0) {
- dvmAddTrackedAlloc(ptr, NULL);
+ dvmAddTrackedAlloc((Object*)ptr, NULL);
}
} else {
/*
LOGE("Could not mmap %zd-byte ashmem region '%s'", bitsLen, name);
return false;
}
- hb->bits = bits;
+ hb->bits = (unsigned long *)bits;
hb->bitsLen = hb->allocLen = bitsLen;
hb->base = (uintptr_t)base;
hb->max = hb->base - 1;
* The callback is not permitted to increase the max of either bitmap.
*/
void dvmHeapBitmapSweepWalk(const HeapBitmap *liveHb, const HeapBitmap *markHb,
+ uintptr_t base, uintptr_t max,
BitmapSweepCallback *callback, void *callbackArg)
{
- static const size_t kPointerBufSize = 128;
- void *pointerBuf[kPointerBufSize];
+ void *pointerBuf[4 * HB_BITS_PER_WORD];
void **pb = pointerBuf;
- size_t index;
size_t i;
+ size_t start, end;
unsigned long *live, *mark;
- uintptr_t offset;
assert(liveHb != NULL);
assert(liveHb->bits != NULL);
assert(liveHb->base == markHb->base);
assert(liveHb->bitsLen == markHb->bitsLen);
assert(callback != NULL);
+ assert(base <= max);
+ assert(base >= liveHb->base);
+ assert(max <= liveHb->max);
if (liveHb->max < liveHb->base) {
/* Easy case; both are obviously empty.
*/
return;
}
- offset = liveHb->max - liveHb->base;
- index = HB_OFFSET_TO_INDEX(offset);
+ start = HB_OFFSET_TO_INDEX(base - liveHb->base);
+ end = HB_OFFSET_TO_INDEX(max - liveHb->base);
live = liveHb->bits;
mark = markHb->bits;
- for (i = 0; i <= index; i++) {
+ for (i = start; i <= end; i++) {
unsigned long garbage = live[i] & ~mark[i];
if (UNLIKELY(garbage != 0)) {
unsigned long highBit = 1 << (HB_BITS_PER_WORD - 1);
}
/* Make sure that there are always enough slots available */
/* for an entire word of 1s. */
- if (kPointerBufSize - (pb - pointerBuf) < HB_BITS_PER_WORD) {
+ if (pb >= &pointerBuf[NELEM(pointerBuf) - HB_BITS_PER_WORD]) {
(*callback)(pb - pointerBuf, pointerBuf, callbackArg);
pb = pointerBuf;
}
* The callback is not permitted to increase the max of either bitmap.
*/
void dvmHeapBitmapSweepWalk(const HeapBitmap *liveHb, const HeapBitmap *markHb,
+ uintptr_t base, uintptr_t max,
BitmapSweepCallback *callback, void *callbackArg);
/*
/* Allocate a descriptor from the heap we just created.
*/
- gcHeap = mspace_malloc(msp, sizeof(*gcHeap));
+ gcHeap = (GcHeap *)mspace_malloc(msp, sizeof(*gcHeap));
if (gcHeap == NULL) {
LOGE_HEAP("Can't allocate heap descriptor\n");
goto fail;
}
memset(gcHeap, 0, sizeof(*gcHeap));
- hs = mspace_malloc(msp, sizeof(*hs));
+ hs = (HeapSource *)mspace_malloc(msp, sizeof(*hs));
if (hs == NULL) {
LOGE_HEAP("Can't allocate heap source\n");
goto fail;
hs->numHeaps = 0;
hs->sawZygote = gDvm.zygote;
hs->hasGcThread = false;
- hs->heapBase = base;
+ hs->heapBase = (char *)base;
hs->heapLength = length;
if (!addNewHeap(hs, msp, absoluteMaxSize)) {
LOGE_HEAP("Can't add initial heap\n");
return total;
}
-static void aliasBitmap(HeapBitmap *dst, HeapBitmap *src,
- uintptr_t base, uintptr_t max) {
- size_t offset;
-
- dst->base = base;
- dst->max = max;
- dst->bitsLen = HB_OFFSET_TO_BYTE_INDEX(max - base) + sizeof(dst->bits);
- /* The exclusive limit from bitsLen is greater than the inclusive max. */
- assert(base + HB_MAX_OFFSET(dst) > max);
- /* The exclusive limit is at most one word of bits beyond max. */
- assert((base + HB_MAX_OFFSET(dst)) - max <=
- HB_OBJECT_ALIGNMENT * HB_BITS_PER_WORD);
- dst->allocLen = dst->bitsLen;
- offset = base - src->base;
- assert(HB_OFFSET_TO_MASK(offset) == 1 << 31);
- dst->bits = &src->bits[HB_OFFSET_TO_INDEX(offset)];
-}
-
-/*
- * Initializes a vector of object and mark bits to the object and mark
- * bits of each heap. The bits are aliased to the heapsource
- * object and mark bitmaps. This routine is used by the sweep code
- * which needs to free each object in the correct heap.
- */
-void dvmHeapSourceGetObjectBitmaps(HeapBitmap liveBits[], HeapBitmap markBits[],
- size_t numHeaps)
+void dvmHeapSourceGetRegions(uintptr_t *base, uintptr_t *max, size_t numHeaps)
{
HeapSource *hs = gHs;
- uintptr_t base, max;
size_t i;
HS_BOILERPLATE();
- assert(numHeaps == hs->numHeaps);
- for (i = 0; i < hs->numHeaps; ++i) {
- base = (uintptr_t)hs->heaps[i].base;
- /* -1 because limit is exclusive but max is inclusive. */
- max = MIN((uintptr_t)hs->heaps[i].limit - 1, hs->markBits.max);
- aliasBitmap(&liveBits[i], &hs->liveBits, base, max);
- aliasBitmap(&markBits[i], &hs->markBits, base, max);
+ assert(numHeaps <= hs->numHeaps);
+ for (i = 0; i < numHeaps; ++i) {
+ base[i] = (uintptr_t)hs->heaps[i].base;
+ max[i] = MIN((uintptr_t)hs->heaps[i].limit - 1, hs->markBits.max);
}
}
return &gHs->liveBits;
}
+/*
+ * Get the bitmap representing all marked objects.
+ */
+HeapBitmap *dvmHeapSourceGetMarkBits(void)
+{
+ HS_BOILERPLATE();
+
+ return &gHs->markBits;
+}
+
void dvmHeapSourceSwapBitmaps(void)
{
HeapBitmap tmp;
heap = ptr2heap(gHs, *ptrs);
numBytes = 0;
if (heap != NULL) {
- mspace *msp = heap->msp;
+ mspace msp = heap->msp;
// Calling mspace_free on shared heaps disrupts sharing too
// much. For heap[0] -- the 'active heap' -- we call
// mspace_free, but on the other heaps we only do some
void dvmHeapSourceShutdown(GcHeap **gcHeap);
/*
- * Initializes a vector of object and mark bits to the object and mark
- * bits of each heap.
+ * Returns the base and inclusive max addresses of the heap source
+ * heaps. The base and max values are suitable for passing directly
+ * to the bitmap sweeping routine.
*/
-void dvmHeapSourceGetObjectBitmaps(HeapBitmap liveBits[], HeapBitmap markBits[],
- size_t numHeaps);
+void dvmHeapSourceGetRegions(uintptr_t *base, uintptr_t *max, size_t numHeaps);
/*
* Get the bitmap representing all live objects.
HeapBitmap *dvmHeapSourceGetLiveBits(void);
/*
+ * Get the bitmap representing all marked objects.
+ */
+HeapBitmap *dvmHeapSourceGetMarkBits(void);
+
+/*
* Gets the begining of the allocation for the HeapSource.
*/
void *dvmHeapSourceGetBase(void);
static const int kLargeHeapRefTableNElems = 1024;
static const int kFinalizableRefDefault = 128;
-void dvmHeapHeapTableFree(void *ptr)
+bool dvmHeapInitHeapRefTable(ReferenceTable *refs)
{
- free(ptr);
-}
-
-#define heapRefTableIsFull(refs) \
- dvmIsReferenceTableFull(refs)
-
-bool dvmHeapInitHeapRefTable(HeapRefTable *refs)
-{
- memset(refs, 0, sizeof(*refs));
return dvmInitReferenceTable(refs, kFinalizableRefDefault, INT_MAX);
}
-/* Frees the array inside the HeapRefTable, not the HeapRefTable itself.
- */
-void dvmHeapFreeHeapRefTable(HeapRefTable *refs)
-{
- dvmClearReferenceTable(refs);
-}
-
/*
* Large, non-contiguous reference tables
*/
/* Find an empty slot for this reference.
*/
prevTable = NULL;
- while (table != NULL && heapRefTableIsFull(&table->refs)) {
+ while (table != NULL && dvmIsReferenceTableFull(&table->refs)) {
prevTable = table;
table = table->next;
}
/* Allocate a new table.
*/
- table = calloc(1, sizeof(LargeHeapRefTable));
+ table = (LargeHeapRefTable *)calloc(1, sizeof(LargeHeapRefTable));
if (table == NULL) {
LOGE_HEAP("Can't allocate a new large ref table\n");
return false;
kLargeHeapRefTableNElems,
INT_MAX)) {
LOGE_HEAP("Can't initialize a new large ref table\n");
- dvmHeapHeapTableFree(table);
+ free(table);
return false;
}
*/
assert(table == *tableP);
assert(table != NULL);
- assert(!heapRefTableIsFull(&table->refs));
+ assert(!dvmIsReferenceTableFull(&table->refs));
*table->refs.nextEntry++ = ref;
return true;
}
-bool dvmHeapAddTableToLargeTable(LargeHeapRefTable **tableP, HeapRefTable *refs)
+bool dvmHeapAddTableToLargeTable(LargeHeapRefTable **tableP, ReferenceTable *refs)
{
LargeHeapRefTable *table;
/* Allocate a node.
*/
- table = calloc(1, sizeof(LargeHeapRefTable));
+ table = (LargeHeapRefTable *)calloc(1, sizeof(LargeHeapRefTable));
if (table == NULL) {
LOGE_HEAP("Can't allocate a new large ref table\n");
return false;
{
while (table != NULL) {
LargeHeapRefTable *next = table->next;
- dvmHeapFreeHeapRefTable(&table->refs);
- dvmHeapHeapTableFree(table);
+ dvmClearReferenceTable(&table->refs);
+ free(table);
table = next;
}
}
obj = NULL;
table = *pTable;
if (table != NULL) {
- HeapRefTable *refs = &table->refs;
+ ReferenceTable *refs = &table->refs;
/* We should never have an empty table node in the list.
*/
if (refs->nextEntry == refs->table) {
*pTable = table->next;
dvmClearReferenceTable(refs);
- dvmHeapHeapTableFree(table);
+ free(table);
}
}
#include "ReferenceTable.h"
-typedef ReferenceTable HeapRefTable;
-typedef struct LargeHeapRefTable LargeHeapRefTable;
-
struct LargeHeapRefTable {
- LargeHeapRefTable *next;
- HeapRefTable refs;
+ struct LargeHeapRefTable *next;
+ ReferenceTable refs;
};
-bool dvmHeapInitHeapRefTable(HeapRefTable *refs);
-void dvmHeapFreeHeapRefTable(HeapRefTable *refs);
+typedef struct LargeHeapRefTable LargeHeapRefTable;
+
+bool dvmHeapInitHeapRefTable(ReferenceTable *refs);
void dvmHeapFreeLargeTable(LargeHeapRefTable *table);
-void dvmHeapHeapTableFree(void *ptr);
bool dvmHeapAddRefToLargeTable(LargeHeapRefTable **tableP, Object *ref);
void dvmHeapMarkLargeTableRefs(LargeHeapRefTable *table);
bool dvmHeapAddTableToLargeTable(LargeHeapRefTable **tableP,
- HeapRefTable *refs);
+ ReferenceTable *refs);
Object *dvmHeapGetNextObjectFromLargeTable(LargeHeapRefTable **pTable);
-#define dvmHeapAddToHeapRefTable(refs, ptr) \
- dvmAddToReferenceTable((refs), (ptr))
-
-#define dvmHeapNumHeapRefTableEntries(refs) \
- dvmReferenceTableEntries(refs)
-
-#define dvmHeapRemoveFromHeapRefTable(refs, ptr) \
- dvmRemoveFromReferenceTable((refs), (refs)->table, (ptr))
-
#endif // _DALVIK_ALLOC_HEAP_TABLE
return false;
}
ctx->finger = NULL;
- ctx->immuneLimit = dvmHeapSourceGetImmuneLimit(mode);
+ ctx->immuneLimit = (char*)dvmHeapSourceGetImmuneLimit(mode);
return true;
}
/*
* Callback applied to root references during the initial root
- * marking. Visited roots are always marked but are only pushed on
- * the mark stack if their address is below the finger.
+ * marking. Marks white objects but does not push them on the mark
+ * stack.
*/
-static void rootMarkObjectVisitor(void *addr, RootType type, u4 thread, void *arg)
+static void rootMarkObjectVisitor(void *addr, RootType type, u4 thread,
+ void *arg)
{
Object *obj;
+ GcMarkContext *ctx;
assert(addr != NULL);
assert(arg != NULL);
obj = *(Object **)addr;
+ ctx = (GcMarkContext *)arg;
if (obj != NULL) {
- markObjectNonNull(obj, arg, false);
+ markObjectNonNull(obj, ctx, false);
}
}
}
/*
- * Callback applied to root references during root remarking. If the
- * root location contains a white reference it is pushed on the mark
- * stack and grayed.
+ * Callback applied to root references during root remarking. Marks
+ * white objects and pushes them on the mark stack.
*/
-static void markObjectVisitor(void *addr, RootType type, u4 thread, void *arg)
+static void rootReMarkObjectVisitor(void *addr, RootType type, u4 thread,
+ void *arg)
{
Object *obj;
+ GcMarkContext *ctx;
assert(addr != NULL);
assert(arg != NULL);
obj = *(Object **)addr;
+ ctx = (GcMarkContext *)arg;
if (obj != NULL) {
- markObjectNonNull(obj, arg, true);
+ markObjectNonNull(obj, ctx, true);
}
}
{
GcMarkContext *ctx = &gDvm.gcHeap->markContext;
assert(ctx->finger == (void *)ULONG_MAX);
- dvmVisitRoots(markObjectVisitor, ctx);
+ dvmVisitRoots(rootReMarkObjectVisitor, ctx);
}
/*
int i;
for (i = 0; i < clazz->ifieldRefCount; ++i, ++field) {
void *addr = BYTE_OFFSET((Object *)obj, field->byteOffset);
- markObject(((JValue *)addr)->l, ctx);
+ Object *ref = (Object *)((JValue *)addr)->l;
+ markObject(ref, ctx);
}
}
}
for (i = 0; i < clazz->sfieldCount; ++i) {
char ch = clazz->sfields[i].field.signature[0];
if (ch == '[' || ch == 'L') {
- markObject(clazz->sfields[i].value.l, ctx);
+ Object *obj = (Object *)clazz->sfields[i].value.l;
+ markObject(obj, ctx);
}
}
}
if (*card != GC_CARD_DIRTY) {
return card;
}
- const u1 *ptr = prevAddr ? prevAddr : dvmAddrFromCard(card);
+ const u1 *ptr = prevAddr ? prevAddr : (u1*)dvmAddrFromCard(card);
const u1 *limit = ptr + GC_CARD_SIZE;
while (ptr < limit) {
Object *obj = nextGrayObject(ptr, limit, markBits);
*/
static void scanBitmapCallback(void *addr, void *finger, void *arg)
{
- GcMarkContext *ctx = arg;
+ GcMarkContext *ctx = (GcMarkContext *)arg;
ctx->finger = (void *)finger;
- scanObject(addr, ctx);
+ scanObject((Object *)addr, ctx);
}
/* Given bitmaps with the root set marked, find and mark all
*/
static void scheduleFinalizations(void)
{
- HeapRefTable newPendingRefs;
+ ReferenceTable newPendingRefs;
LargeHeapRefTable *finRefs = gDvm.gcHeap->finalizableRefs;
Object **ref;
Object **lastRef;
lastRef = finRefs->refs.nextEntry;
while (ref < lastRef) {
if (!isMarked(*ref, ctx)) {
- if (!dvmHeapAddToHeapRefTable(&newPendingRefs, *ref)) {
+ if (!dvmAddToReferenceTable(&newPendingRefs, *ref)) {
//TODO: add the current table and allocate
// a new, smaller one.
LOGE_GC("scheduleFinalizations(): "
"no room for any more pending finalizations: %zd",
- dvmHeapNumHeapRefTableEntries(&newPendingRefs));
+ dvmReferenceTableEntries(&newPendingRefs));
dvmAbort();
}
newPendCount++;
static void sweepBitmapCallback(size_t numPtrs, void **ptrs, void *arg)
{
- SweepContext *ctx = arg;
+ SweepContext *ctx = (SweepContext *)arg;
if (ctx->isConcurrent) {
dvmLockHeap();
void dvmHeapSweepUnmarkedObjects(GcMode mode, bool isConcurrent,
size_t *numObjects, size_t *numBytes)
{
- HeapBitmap currMark[HEAP_SOURCE_MAX_HEAP_COUNT];
- HeapBitmap currLive[HEAP_SOURCE_MAX_HEAP_COUNT];
+ uintptr_t base[HEAP_SOURCE_MAX_HEAP_COUNT];
+ uintptr_t max[HEAP_SOURCE_MAX_HEAP_COUNT];
SweepContext ctx;
- size_t numBitmaps, numSweepBitmaps;
+ HeapBitmap *prevLive, *prevMark;
+ size_t numHeaps, numSweepHeaps;
size_t i;
- numBitmaps = dvmHeapSourceGetNumHeaps();
- dvmHeapSourceGetObjectBitmaps(currLive, currMark, numBitmaps);
+ numHeaps = dvmHeapSourceGetNumHeaps();
+ dvmHeapSourceGetRegions(base, max, numHeaps);
if (mode == GC_PARTIAL) {
- numSweepBitmaps = 1;
- assert((uintptr_t)gDvm.gcHeap->markContext.immuneLimit == currLive[0].base);
+ assert((uintptr_t)gDvm.gcHeap->markContext.immuneLimit == base[0]);
+ numSweepHeaps = 1;
} else {
- numSweepBitmaps = numBitmaps;
+ numSweepHeaps = numHeaps;
}
ctx.numObjects = ctx.numBytes = 0;
ctx.isConcurrent = isConcurrent;
- for (i = 0; i < numSweepBitmaps; i++) {
- HeapBitmap* prevLive = &currMark[i];
- HeapBitmap* prevMark = &currLive[i];
- dvmHeapBitmapSweepWalk(prevLive, prevMark, sweepBitmapCallback, &ctx);
+ prevLive = dvmHeapSourceGetMarkBits();
+ prevMark = dvmHeapSourceGetLiveBits();
+ for (i = 0; i < numSweepHeaps; ++i) {
+ dvmHeapBitmapSweepWalk(prevLive, prevMark, base[i], max[i],
+ sweepBitmapCallback, &ctx);
}
*numObjects = ctx.numObjects;
*numBytes = ctx.numBytes;
static void dumpReferencesCallback(void *ptr, void *arg)
{
- Object *obj = arg;
+ Object *obj = (Object *)arg;
if (ptr == obj) {
return;
}
- dvmVisitObject(dumpReferencesVisitor, ptr, &obj);
+ dvmVisitObject(dumpReferencesVisitor, (Object *)ptr, &obj);
if (obj == NULL) {
LOGD("Found %p in the heap @ %p", arg, ptr);
- dvmDumpObject(ptr);
+ dvmDumpObject((Object *)ptr);
}
}
isValid = dvmIsValidObject(obj);
}
if (!isValid) {
- Object **parent = arg;
+ Object **parent = (Object **)arg;
if (*parent != NULL) {
LOGE("Verify of object %p failed", *parent);
dvmDumpObject(*parent);
*/
static void verifyBitmapCallback(void *ptr, void *arg)
{
- dvmVerifyObject(ptr);
+ dvmVerifyObject((Object *)ptr);
}
/*
assert(thread != NULL);
threadId = thread->threadId;
fp = (u4 *)thread->curFrame;
- for (; fp != NULL; fp = saveArea->prevFrame) {
+ for (; fp != NULL; fp = (u4 *)saveArea->prevFrame) {
Method *method;
saveArea = SAVEAREA_FROM_FP(fp);
method = (Method *)saveArea->method;
while (refOffsets != 0) {
size_t rshift = CLZ(refOffsets);
size_t offset = CLASS_OFFSET_FROM_CLZ(rshift);
- Object **ref = BYTE_OFFSET(obj, offset);
+ Object **ref = (Object **)BYTE_OFFSET(obj, offset);
(*visitor)(ref, arg);
refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
}
int i;
for (i = 0; i < clazz->ifieldRefCount; ++i, ++field) {
size_t offset = field->byteOffset;
- Object **ref = BYTE_OFFSET(obj, offset);
+ Object **ref = (Object **)BYTE_OFFSET(obj, offset);
(*visitor)(ref, arg);
}
}
assert(obj->clazz != NULL);
visitDataObject(visitor, obj, arg);
size_t offset = gDvm.offJavaLangRefReference_referent;
- Object **ref = BYTE_OFFSET(obj, offset);
+ Object **ref = (Object **)BYTE_OFFSET(obj, offset);
(*visitor)(ref, arg);
}
* some string-peeling and wouldn't need to compute hashes.
*/
#include "Dalvik.h"
+#include "analysis/Liveness.h"
#include "analysis/CodeVerify.h"
#include "analysis/Optimize.h"
#include "analysis/RegisterMap.h"
# define DEAD_CODE_SCAN false
#endif
-static bool gDebugVerbose = false; // TODO: remove this
+static bool gDebugVerbose = false;
-#if 0
-int gDvm__totalInstr = 0;
-int gDvm__gcInstr = 0;
-int gDvm__gcData = 0;
-int gDvm__gcSimpleData = 0;
-#endif
-
-/*
- * Selectively enable verbose debug logging -- use this to activate
- * dumpRegTypes() calls for all instructions in the specified method.
- */
-static inline bool doVerboseLogging(const Method* meth) {
- return false; /* COMMENT OUT to enable verbose debugging */
-
- const char* cd = "Lcom/android/bluetooth/opp/BluetoothOppService;";
- const char* mn = "scanFile";
- const char* sg = "(Landroid/database/Cursor;I)Z";
- return (strcmp(meth->clazz->descriptor, cd) == 0 &&
- dvmCompareNameDescriptorAndMethod(mn, sg, meth) == 0);
-}
-
-#define SHOW_REG_DETAILS (0 /*| DRT_SHOW_REF_TYPES | DRT_SHOW_LOCALS*/)
+#define SHOW_REG_DETAILS \
+ (0 | DRT_SHOW_LIVENESS /*| DRT_SHOW_REF_TYPES | DRT_SHOW_LOCALS*/)
/*
* We need an extra "pseudo register" to hold the return type briefly. It
const DecodedInstruction* pDecInsn, VerifyError* pFailure);
static void verifyRegisterType(const RegisterLine* registerLine, \
u4 vsrc, RegType checkType, VerifyError* pFailure);
-static bool doCodeVerification(const Method* meth, InsnFlags* insnFlags,\
- RegisterTable* regTable, UninitInstanceMap* uninitMap);
+static bool doCodeVerification(VerifierData* vdata, RegisterTable* regTable);
static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags,\
RegisterTable* regTable, int insnIdx, UninitInstanceMap* uninitMap,
int* pStartGuess);
static ClassObject* findCommonSuperclass(ClassObject* c1, ClassObject* c2);
-static void dumpRegTypes(const Method* meth, const InsnFlags* insnFlags,\
+static void dumpRegTypes(const VerifierData* vdata, \
const RegisterLine* registerLine, int addr, const char* addrName,
const UninitInstanceMap* uninitMap, int displayFlags);
DRT_SIMPLE = 0,
DRT_SHOW_REF_TYPES = 0x01,
DRT_SHOW_LOCALS = 0x02,
+ DRT_SHOW_LIVENESS = 0x04,
};
*/
int size = offsetof(UninitInstanceMap, map) +
newInstanceCount * sizeof(uninitMap->map[0]);
- uninitMap = calloc(1, size);
+ uninitMap = (UninitInstanceMap*)calloc(1, size);
if (uninitMap == NULL)
return NULL;
uninitMap->numEntries = newInstanceCount;
} else {
if (gDebugVerbose) {
LOGVV("MERGE into 0x%04x\n", nextInsn);
- //dumpRegTypes(meth, insnFlags, targetRegs, 0, "targ", NULL, 0);
- //dumpRegTypes(meth, insnFlags, workRegs, 0, "work", NULL, 0);
+ //dumpRegTypes(vdata, targetRegs, 0, "targ", NULL, 0);
+ //dumpRegTypes(vdata, workRegs, 0, "work", NULL, 0);
}
/* merge registers, set Changed only if different */
RegisterLine* targetLine = getRegisterLine(regTable, nextInsn);
if (gDebugVerbose) {
//LOGI(" RESULT (changed=%d)\n", changed);
- //dumpRegTypes(meth, insnFlags, targetRegs, 0, "rslt", NULL, 0);
+ //dumpRegTypes(vdata, targetRegs, 0, "rslt", NULL, 0);
}
#ifdef VERIFIER_STATS
gDvm.verifierStats.mergeRegCount++;
* what's in which register, but for verification purposes we only need to
* store it at branch target addresses (because we merge into that).
*
- * By zeroing out the storage we are effectively initializing the register
- * information to kRegTypeUnknown.
+ * By zeroing out the regType storage we are effectively initializing the
+ * register information to kRegTypeUnknown.
*
* We jump through some hoops here to minimize the total number of
* allocations we have to perform per method verified.
/*
* Populate the sparse register line table.
+ *
+ * There is a RegisterLine associated with every address, but not
+ * every RegisterLine has non-NULL pointers to storage for its fields.
*/
- u1* storage = regTable->lineAlloc;
+ u1* storage = (u1*)regTable->lineAlloc;
for (i = 0; i < insnsSize; i++) {
bool interesting;
}
/*
+ * Free up any "hairy" structures associated with register lines.
+ */
+static void freeRegisterLineInnards(VerifierData* vdata)
+{
+ unsigned int idx;
+
+ if (vdata->registerLines == NULL)
+ return;
+
+ for (idx = 0; idx < vdata->insnsSize; idx++) {
+ BitVector* liveRegs = vdata->registerLines[idx].liveRegs;
+ if (liveRegs != NULL)
+ dvmFreeBitVector(liveRegs);
+ }
+}
+
+
+/*
* Verify that the arguments in a filled-new-array instruction are valid.
*
* "resClass" is the class refered to by pDecInsn->vB.
int insnIdx, VerifyError failure)
{
VerifyErrorRefType refType;
- const u2* oldInsns = meth->insns + insnIdx;
- u2 oldInsn = *oldInsns;
+ u2* oldInsns = (u2*) meth->insns + insnIdx;
bool result = false;
if (gDvm.optimizing)
LOGD("Weird: RFI during dexopt?");
- //LOGD(" was 0x%04x\n", oldInsn);
- u2* newInsns = (u2*) meth->insns + insnIdx;
-
/*
* Generate the new instruction out of the old.
*
* First, make sure this is an instruction we're expecting to stomp on.
*/
- switch (oldInsn & 0xff) {
+ Opcode opcode = dexOpcodeFromCodeUnit(*oldInsns);
+ switch (opcode) {
case OP_CONST_CLASS: // insn[1] == class ref, 2 bytes
case OP_CHECK_CAST:
case OP_INSTANCE_OF:
default:
/* could handle this in a generic way, but this is probably safer */
- LOG_VFY("GLITCH: verifier asked to replace opcode 0x%02x\n",
- oldInsn & 0xff);
+ LOG_VFY("GLITCH: verifier asked to replace opcode 0x%02x\n", opcode);
goto bail;
}
+ assert((dexGetFlagsFromOpcode(opcode) & kInstrCanThrow) != 0);
+
/* write a NOP over the third code unit, if necessary */
int width = dvmInsnGetWidth(insnFlags, insnIdx);
switch (width) {
/* nothing to do */
break;
case 3:
- dvmDexChangeDex2(meth->clazz->pDvmDex, newInsns+2, OP_NOP);
- //newInsns[2] = OP_NOP;
+ dvmDexChangeDex2(meth->clazz->pDvmDex, oldInsns+2, OP_NOP);
break;
default:
/* whoops */
/* encode the opcode, with the failure code in the high byte */
u2 newVal = OP_THROW_VERIFICATION_ERROR |
(failure << 8) | (refType << (8 + kVerifyErrorRefTypeShift));
- //newInsns[0] = newVal;
- dvmDexChangeDex2(meth->clazz->pDvmDex, newInsns, newVal);
+ dvmDexChangeDex2(meth->clazz->pDvmDex, oldInsns, newVal);
result = true;
generateRegisterMap ? kTrackRegsGcPoints : kTrackRegsBranches))
goto bail;
- vdata->registerLines = NULL; /* don't set this until we need it */
+ vdata->registerLines = regTable.registerLines;
+
+ /*
+ * Perform liveness analysis.
+ *
+ * We can do this before or after the main verifier pass. The choice
+ * affects whether or not we see the effects of verifier instruction
+ * changes, i.e. substitution of throw-verification-error.
+ *
+ * In practice the ordering doesn't really matter, because T-V-E
+ * just prunes "can continue", creating regions of dead code (with
+ * corresponding register map data that will never be used).
+ */
+ if (generateRegisterMap &&
+ gDvm.registerMapMode == kRegisterMapModeLivePrecise)
+ {
+ /*
+ * Compute basic blocks and predecessor lists.
+ */
+ if (!dvmComputeVfyBasicBlocks(vdata))
+ goto bail;
+
+ /*
+ * Compute liveness.
+ */
+ if (!dvmComputeLiveness(vdata))
+ goto bail;
+ }
/*
* Initialize the types of the registers that correspond to the
/*
* Run the verifier.
*/
- if (!doCodeVerification(meth, vdata->insnFlags, ®Table,
- vdata->uninitMap))
+ if (!doCodeVerification(vdata, ®Table))
goto bail;
/*
* Generate a register map.
*/
if (generateRegisterMap) {
- vdata->registerLines = regTable.registerLines;
-
RegisterMap* pMap = dvmGenerateRegisterMapV(vdata);
if (pMap != NULL) {
/*
result = true;
bail:
+ freeRegisterLineInnards(vdata);
free(regTable.registerLines);
free(regTable.lineAlloc);
return result;
* instruction if a register contains an uninitialized instance created
* by that same instrutcion.
*/
-static bool doCodeVerification(const Method* meth, InsnFlags* insnFlags,
- RegisterTable* regTable, UninitInstanceMap* uninitMap)
+static bool doCodeVerification(VerifierData* vdata, RegisterTable* regTable)
{
+ const Method* meth = vdata->method;
+ InsnFlags* insnFlags = vdata->insnFlags;
+ UninitInstanceMap* uninitMap = vdata->uninitMap;
const int insnsSize = dvmGetMethodInsnsSize(meth);
bool result = false;
bool debugVerbose = false;
*/
dvmInsnSetChanged(insnFlags, 0, true);
- if (doVerboseLogging(meth)) {
+ if (dvmWantVerboseVerification(meth)) {
IF_LOGI() {
char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
LOGI("Now verifying: %s.%s %s (ins=%d regs=%d)\n",
LOG_VFY("HUH? workLine diverged in %s.%s %s\n",
meth->clazz->descriptor, meth->name, desc);
free(desc);
- dumpRegTypes(meth, insnFlags, registerLine, 0, "work",
+ dumpRegTypes(vdata, registerLine, 0, "work",
uninitMap, DRT_SHOW_REF_TYPES | DRT_SHOW_LOCALS);
- dumpRegTypes(meth, insnFlags, registerLine, 0, "insn",
+ dumpRegTypes(vdata, registerLine, 0, "insn",
uninitMap, DRT_SHOW_REF_TYPES | DRT_SHOW_LOCALS);
}
#endif
}
if (debugVerbose) {
- dumpRegTypes(meth, insnFlags, ®Table->workLine, insnIdx,
+ dumpRegTypes(vdata, ®Table->workLine, insnIdx,
NULL, uninitMap, SHOW_REG_DETAILS);
}
RegisterLine* workLine = ®Table->workLine;
const DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
ClassObject* resClass;
- int branchTarget = 0;
+ s4 branchTarget = 0;
const int insnRegCount = meth->registersSize;
RegType tmpType;
DecodedInstruction decInsn;
if ((nextFlags & kInstrCanBranch) != 0) {
bool isConditional;
- if (!dvmGetBranchTarget(meth, insnFlags, insnIdx, &branchTarget,
+ if (!dvmGetBranchOffset(meth, insnFlags, insnIdx, &branchTarget,
&isConditional))
{
/* should never happen after static verification */
/*
* Dump the register types for the specifed address to the log file.
*/
-static void dumpRegTypes(const Method* meth, const InsnFlags* insnFlags,
+static void dumpRegTypes(const VerifierData* vdata,
const RegisterLine* registerLine, int addr, const char* addrName,
const UninitInstanceMap* uninitMap, int displayFlags)
{
+ const Method* meth = vdata->method;
+ const InsnFlags* insnFlags = vdata->insnFlags;
const RegType* addrRegs = registerLine->regTypes;
int regCount = meth->registersSize;
int fullRegCount = regCount + kExtraRegs;
LOGI("%c0x%04x %s mst=%d\n", branchTarget ? '>' : ' ',
addr, regChars, registerLine->monitorStackTop);
}
+ if (displayFlags & DRT_SHOW_LIVENESS) {
+ /*
+ * We can't use registerLine->liveRegs because it might be the
+ * "work line" rather than the copy from RegisterTable.
+ */
+ BitVector* liveRegs = vdata->registerLines[addr].liveRegs;
+ if (liveRegs != NULL) {
+ char liveChars[regCharSize + 1];
+ memset(liveChars, ' ', regCharSize);
+ liveChars[regCharSize] = '\0';
+
+ for (i = 0; i < regCount; i++) {
+ bool isLive = dvmIsBitSet(liveRegs, i);
+ liveChars[i + 1 + (i / 4)] = isLive ? '+' : '-';
+ }
+ LOGI(" %s\n", liveChars);
+ } else {
+ LOGI(" %c\n", '#');
+ }
+ }
if (displayFlags & DRT_SHOW_REF_TYPES) {
for (i = 0; i < regCount + kExtraRegs; i++) {
#define _DALVIK_CODEVERIFY
#include "analysis/VerifySubs.h"
+#include "analysis/VfyBasicBlock.h"
/*
* instruction. We track the status of all registers, and (if the method
* has any monitor-enter instructions) maintain a stack of entered monitors
* (identified by code unit offset).
+ *
+ * If live-precise register maps are enabled, the "liveRegs" vector will
+ * be populated. Unlike the other lists of registers here, we do not
+ * track the liveness of the method result register (which is not visible
+ * to the GC).
*/
typedef struct {
RegType* regTypes;
MonitorEntries* monitorEntries;
u4* monitorStack;
unsigned int monitorStackTop;
+ BitVector* liveRegs;
} RegisterLine;
/*
*/
size_t newInstanceCount;
size_t monitorEnterCount;
+
+ /*
+ * Array of pointers to basic blocks, one entry per code unit. Used
+ * for liveness analysis.
+ */
+ VfyBasicBlock** basicBlocks;
} VerifierData;
LOGW("ANDROID_ROOT not set, defaulting to /system\n");
androidRoot = "/system";
}
- execFile = malloc(strlen(androidRoot) + strlen(kDexOptBin) + 1);
+ execFile = (char*)malloc(strlen(androidRoot) + strlen(kDexOptBin) + 1);
strcpy(execFile, androidRoot);
strcat(execFile, kDexOptBin);
bufLen += 4*4 + numDeps * (4+kSHA1DigestLen);
- buf = malloc(bufLen);
+ buf = (u1*)malloc(bufLen);
set4LE(buf+0, modWhen);
set4LE(buf+4, crc);
i += width;
insns += width;
}
- if (i != (int) dvmGetMethodInsnsSize(meth)) {
+ if (i != (int) vdata->insnsSize) {
LOG_VFY_METH(meth, "VFY: code did not end where expected (%d vs. %d)\n",
i, dvmGetMethodInsnsSize(meth));
goto bail;
const DexCode* pCode = dvmGetMethodCode(meth);
u4 triesSize = pCode->triesSize;
const DexTry* pTries;
- u4 handlersSize;
- u4 offset;
- u4 i;
+ u4 idx;
if (triesSize == 0) {
return true;
}
pTries = dexGetTries(pCode);
- handlersSize = dexGetHandlersSize(pCode);
- for (i = 0; i < triesSize; i++) {
- const DexTry* pTry = &pTries[i];
+ for (idx = 0; idx < triesSize; idx++) {
+ const DexTry* pTry = &pTries[idx];
u4 start = pTry->startAddr;
u4 end = start + pTry->insnCount;
u4 addr;
}
/* Iterate over each of the handlers to verify target addresses. */
- offset = dexGetFirstHandlerOffset(pCode);
- for (i = 0; i < handlersSize; i++) {
+ u4 handlersSize = dexGetHandlersSize(pCode);
+ u4 offset = dexGetFirstHandlerOffset(pCode);
+ for (idx = 0; idx < handlersSize; idx++) {
DexCatchIterator iterator;
dexCatchIteratorInit(&iterator, pCode, offset);
vdata.insnRegCount = meth->registersSize;
vdata.insnFlags = NULL;
vdata.uninitMap = NULL;
+ vdata.basicBlocks = NULL;
/*
* If there aren't any instructions, make sure that's expected, then
* TODO: Consider keeping a reusable pre-allocated array sitting
* around for smaller methods.
*/
- vdata.insnFlags = (InsnFlags*)
- calloc(dvmGetMethodInsnsSize(meth), sizeof(InsnFlags));
+ vdata.insnFlags = (InsnFlags*) calloc(vdata.insnsSize, sizeof(InsnFlags));
if (vdata.insnFlags == NULL)
goto bail;
/*
* Set the "in try" flags for all instructions guarded by a "try" block.
+ * Also sets the "branch target" flag on exception handlers.
*/
if (!scanTryCatchBlocks(meth, vdata.insnFlags))
goto bail;
/*
- * Perform static instruction verification.
+ * Perform static instruction verification. Also sets the "branch
+ * target" flags.
*/
if (!verifyInstructions(&vdata))
goto bail;
result = true;
bail:
+ dvmFreeVfyBasicBlocks(&vdata);
dvmFreeUninitInstanceMap(vdata.uninitMap);
free(vdata.insnFlags);
return result;
int curOffset, bool selfOkay)
{
const int insnCount = dvmGetMethodInsnsSize(meth);
- int offset, absOffset;
+ s4 offset, absOffset;
bool isConditional;
- if (!dvmGetBranchTarget(meth, insnFlags, curOffset, &offset,
+ if (!dvmGetBranchOffset(meth, insnFlags, curOffset, &offset,
&isConditional))
return false;
* This instruction is probably a GC point. Branch instructions
* only qualify if they go backward, so for those we need to
* check the offset.
+ *
+ * TODO: we could also scan the targets of a "switch" statement,
+ * and if none of them branch backward we could ignore that
+ * instruction as well.
*/
- int offset;
+ s4 offset;
bool unused;
if ((opFlags & kInstrCanBranch) != 0) {
/*
* component was tagged with kVfyBranch, but it's easier
* to just grab it again than cart the state around.
*/
- if (!dvmGetBranchTarget(meth, insnFlags, codeOffset, &offset,
+ if (!dvmGetBranchOffset(meth, insnFlags, codeOffset, &offset,
&unused))
{
/* should never happen */
--- /dev/null
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Liveness analysis for Dalvik bytecode.
+ */
+#include "Dalvik.h"
+#include "analysis/Liveness.h"
+#include "analysis/CodeVerify.h"
+
+static bool processInstruction(VerifierData* vdata, u4 curIdx,
+ BitVector* workBits);
+static bool markDebugLocals(VerifierData* vdata);
+static void dumpLiveState(const VerifierData* vdata, u4 curIdx,
+ const BitVector* workBits);
+
+
+/*
+ * Create a table of instruction widths that indicate the width of the
+ * *previous* instruction. The values are copied from the width table
+ * in "vdata", not derived from the instruction stream.
+ *
+ * Caller must free the return value.
+ */
+static InstructionWidth* createBackwardWidthTable(VerifierData* vdata)
+{
+ InstructionWidth* widths;
+
+ widths = (InstructionWidth*)
+ calloc(vdata->insnsSize, sizeof(InstructionWidth));
+ if (widths == NULL)
+ return NULL;
+
+ unsigned int idx;
+ u4 insnWidth = 0;
+ for (idx = 0; idx < vdata->insnsSize; ) {
+ widths[idx] = insnWidth;
+ insnWidth = dvmInsnGetWidth(vdata->insnFlags, idx);
+ idx += insnWidth;
+ }
+
+ return widths;
+}
+
+/*
+ * Compute the "liveness" of every register at all GC points.
+ */
+bool dvmComputeLiveness(VerifierData* vdata)
+{
+ const InsnFlags* insnFlags = vdata->insnFlags;
+ InstructionWidth* backwardWidth;
+ VfyBasicBlock* startGuess = NULL;
+ BitVector* workBits;
+ bool result = false;
+
+ bool verbose = false; //= dvmWantVerboseVerification(vdata->method);
+ if (verbose) {
+ const Method* meth = vdata->method;
+ LOGI("Computing liveness for %s.%s:%s\n",
+ meth->clazz->descriptor, meth->name, meth->shorty);
+ }
+
+ assert(vdata->registerLines != NULL);
+
+ backwardWidth = createBackwardWidthTable(vdata);
+ if (backwardWidth == NULL)
+ goto bail;
+
+ /*
+ * Allocate space for intra-block work set. Does not include space
+ * for method result "registers", which aren't visible to the GC.
+ * (They would be made live by move-result and then die on the
+ * instruction immediately before it.)
+ */
+ workBits = dvmAllocBitVector(vdata->insnRegCount, false);
+ if (workBits == NULL)
+ goto bail;
+
+ /*
+ * We continue until all blocks have been visited, and no block
+ * requires further attention ("visited" is set and "changed" is
+ * clear).
+ *
+ * TODO: consider creating a "dense" array of basic blocks to make
+ * the walking faster.
+ */
+ int iter = 0;
+ while (true) {
+ VfyBasicBlock* workBlock = NULL;
+ unsigned int idx;
+
+ if (iter++ > 100000) {
+ LOG_VFY_METH(vdata->method, "oh dear");
+ dvmAbort();
+ }
+
+ /*
+ * If a block is marked "changed", we stop and handle it. If it
+ * just hasn't been visited yet, we remember it but keep searching
+ * for one that has been changed.
+ *
+ * The thought here is that this is more likely to let us work
+ * from end to start, which reduces the amount of re-evaluation
+ * required (both by using "changed" as a work list, and by picking
+ * un-visited blocks from the tail end of the method).
+ */
+ if (startGuess != NULL) {
+ assert(startGuess->changed);
+ workBlock = startGuess;
+ } else {
+ for (idx = 0; idx < vdata->insnsSize; idx++) {
+ VfyBasicBlock* block = vdata->basicBlocks[idx];
+ if (block == NULL)
+ continue;
+
+ if (block->changed) {
+ workBlock = block;
+ break;
+ } else if (!block->visited) {
+ workBlock = block;
+ }
+ }
+ }
+
+ if (workBlock == NULL) {
+ /* all done */
+ break;
+ }
+
+ assert(workBlock->changed || !workBlock->visited);
+ startGuess = NULL;
+
+ /*
+ * Load work bits. These represent the liveness of registers
+ * after the last instruction in the block has finished executing.
+ */
+ assert(workBlock->liveRegs != NULL);
+ dvmCopyBitVector(workBits, workBlock->liveRegs);
+ if (verbose) {
+ LOGI("Loaded work bits from last=0x%04x\n", workBlock->lastAddr);
+ dumpLiveState(vdata, 0xfffd, workBlock->liveRegs);
+ dumpLiveState(vdata, 0xffff, workBits);
+ }
+
+ /*
+ * Process a single basic block.
+ *
+ * If this instruction is a GC point, we want to save the result
+ * in the RegisterLine.
+ *
+ * We don't break basic blocks on every GC point -- in particular,
+ * instructions that might throw but have no "try" block don't
+ * end a basic block -- so there could be more than one GC point
+ * in a given basic block.
+ *
+ * We could change this, but it turns out to be not all that useful.
+ * At first glance it appears that we could share the liveness bit
+ * vector between the basic block struct and the register line,
+ * but the basic block needs to reflect the state *after* the
+ * instruction has finished, while the GC points need to describe
+ * the state before the instruction starts.
+ */
+ u4 curIdx = workBlock->lastAddr;
+ while (true) {
+ if (!processInstruction(vdata, curIdx, workBits))
+ goto bail;
+
+ if (verbose) {
+ dumpLiveState(vdata, curIdx + 0x8000, workBits);
+ }
+
+ if (dvmInsnIsGcPoint(insnFlags, curIdx)) {
+ BitVector* lineBits = vdata->registerLines[curIdx].liveRegs;
+ if (lineBits == NULL) {
+ lineBits = vdata->registerLines[curIdx].liveRegs =
+ dvmAllocBitVector(vdata->insnRegCount, false);
+ }
+ dvmCopyBitVector(lineBits, workBits);
+ }
+
+ if (curIdx == workBlock->firstAddr)
+ break;
+ assert(curIdx >= backwardWidth[curIdx]);
+ curIdx -= backwardWidth[curIdx];
+ }
+
+ workBlock->visited = true;
+ workBlock->changed = false;
+
+ if (verbose) {
+ dumpLiveState(vdata, curIdx, workBits);
+ }
+
+ /*
+ * Merge changes to all predecessors. If the new bits don't match
+ * the old bits, set the "changed" flag.
+ */
+ PointerSet* preds = workBlock->predecessors;
+ size_t numPreds = dvmPointerSetGetCount(preds);
+ unsigned int predIdx;
+
+ for (predIdx = 0; predIdx < numPreds; predIdx++) {
+ VfyBasicBlock* pred =
+ (VfyBasicBlock*) dvmPointerSetGetEntry(preds, predIdx);
+
+ pred->changed = dvmCheckMergeBitVectors(pred->liveRegs, workBits);
+ if (verbose) {
+ LOGI("merging cur=%04x into pred last=%04x (ch=%d)\n",
+ curIdx, pred->lastAddr, pred->changed);
+ dumpLiveState(vdata, 0xfffa, pred->liveRegs);
+ dumpLiveState(vdata, 0xfffb, workBits);
+ }
+
+ /*
+ * We want to set the "changed" flag on unvisited predecessors
+ * as a way of guiding the verifier through basic blocks in
+ * a reasonable order. We can't count on variable liveness
+ * changing, so we force "changed" to true even if it hasn't.
+ */
+ if (!pred->visited)
+ pred->changed = true;
+
+ /*
+ * Keep track of one of the changed blocks so we can start
+ * there instead of having to scan through the list.
+ */
+ if (pred->changed)
+ startGuess = pred;
+ }
+ }
+
+#ifndef NDEBUG
+ /*
+ * Sanity check: verify that all GC point register lines have a
+ * liveness bit vector allocated. Also, we're not expecting non-GC
+ * points to have them.
+ */
+ u4 checkIdx;
+ for (checkIdx = 0; checkIdx < vdata->insnsSize; ) {
+ if (dvmInsnIsGcPoint(insnFlags, checkIdx)) {
+ if (vdata->registerLines[checkIdx].liveRegs == NULL) {
+ LOG_VFY_METH(vdata->method,
+ "GLITCH: no liveRegs for GC point 0x%04x\n", checkIdx);
+ dvmAbort();
+ }
+ } else if (vdata->registerLines[checkIdx].liveRegs != NULL) {
+ LOG_VFY_METH(vdata->method,
+ "GLITCH: liveRegs for non-GC point 0x%04x\n", checkIdx);
+ dvmAbort();
+ }
+ u4 insnWidth = dvmInsnGetWidth(insnFlags, checkIdx);
+ checkIdx += insnWidth;
+ }
+#endif
+
+ /*
+ * Factor in the debug info, if any.
+ */
+ if (!markDebugLocals(vdata))
+ goto bail;
+
+ result = true;
+
+bail:
+ free(backwardWidth);
+ return result;
+}
+
+
+/*
+ * Add a register to the LIVE set.
+ */
+static inline void GEN(BitVector* workBits, u4 regIndex)
+{
+ dvmSetBit(workBits, regIndex);
+}
+
+/*
+ * Add a register pair to the LIVE set.
+ */
+static inline void GENW(BitVector* workBits, u4 regIndex)
+{
+ dvmSetBit(workBits, regIndex);
+ dvmSetBit(workBits, regIndex+1);
+}
+
+/*
+ * Remove a register from the LIVE set.
+ */
+static inline void KILL(BitVector* workBits, u4 regIndex)
+{
+ dvmClearBit(workBits, regIndex);
+}
+
+/*
+ * Remove a register pair from the LIVE set.
+ */
+static inline void KILLW(BitVector* workBits, u4 regIndex)
+{
+ dvmClearBit(workBits, regIndex);
+ dvmClearBit(workBits, regIndex+1);
+}
+
+/*
+ * Process a single instruction.
+ *
+ * Returns "false" if something goes fatally wrong.
+ */
+static bool processInstruction(VerifierData* vdata, u4 insnIdx,
+ BitVector* workBits)
+{
+ const Method* meth = vdata->method;
+ const u2* insns = meth->insns + insnIdx;
+ DecodedInstruction decInsn;
+
+ dexDecodeInstruction(insns, &decInsn);
+
+ /*
+ * Add registers to the "GEN" or "KILL" sets. We want to do KILL
+ * before GEN to handle cases where the source and destination
+ * register is the same.
+ */
+ switch (decInsn.opcode) {
+ case OP_NOP:
+ case OP_RETURN_VOID:
+ case OP_GOTO:
+ case OP_GOTO_16:
+ case OP_GOTO_32:
+ /* no registers are used */
+ break;
+
+ case OP_RETURN:
+ case OP_RETURN_OBJECT:
+ case OP_MONITOR_ENTER:
+ case OP_MONITOR_EXIT:
+ case OP_CHECK_CAST:
+ case OP_THROW:
+ case OP_PACKED_SWITCH:
+ case OP_SPARSE_SWITCH:
+ case OP_FILL_ARRAY_DATA:
+ case OP_IF_EQZ:
+ case OP_IF_NEZ:
+ case OP_IF_LTZ:
+ case OP_IF_GEZ:
+ case OP_IF_GTZ:
+ case OP_IF_LEZ:
+ case OP_SPUT:
+ case OP_SPUT_VOLATILE:
+ case OP_SPUT_BOOLEAN:
+ case OP_SPUT_BYTE:
+ case OP_SPUT_CHAR:
+ case OP_SPUT_SHORT:
+ case OP_SPUT_OBJECT:
+ case OP_SPUT_OBJECT_VOLATILE:
+ /* action <- vA */
+ GEN(workBits, decInsn.vA);
+ break;
+
+ case OP_RETURN_WIDE:
+ case OP_SPUT_WIDE:
+ case OP_SPUT_WIDE_VOLATILE:
+ /* action <- vA(wide) */
+ GENW(workBits, decInsn.vA);
+ break;
+
+ case OP_IF_EQ:
+ case OP_IF_NE:
+ case OP_IF_LT:
+ case OP_IF_GE:
+ case OP_IF_GT:
+ case OP_IF_LE:
+ case OP_IPUT:
+ case OP_IPUT_VOLATILE:
+ case OP_IPUT_BOOLEAN:
+ case OP_IPUT_BYTE:
+ case OP_IPUT_CHAR:
+ case OP_IPUT_SHORT:
+ case OP_IPUT_OBJECT:
+ case OP_IPUT_OBJECT_VOLATILE:
+ /* action <- vA, vB */
+ GEN(workBits, decInsn.vA);
+ GEN(workBits, decInsn.vB);
+ break;
+
+ case OP_IPUT_WIDE:
+ case OP_IPUT_WIDE_VOLATILE:
+ /* action <- vA(wide), vB */
+ GENW(workBits, decInsn.vA);
+ GEN(workBits, decInsn.vB);
+ break;
+
+ case OP_APUT:
+ case OP_APUT_BOOLEAN:
+ case OP_APUT_BYTE:
+ case OP_APUT_CHAR:
+ case OP_APUT_SHORT:
+ case OP_APUT_OBJECT:
+ /* action <- vA, vB, vC */
+ GEN(workBits, decInsn.vA);
+ GEN(workBits, decInsn.vB);
+ GEN(workBits, decInsn.vC);
+ break;
+
+ case OP_APUT_WIDE:
+ /* action <- vA(wide), vB, vC */
+ GENW(workBits, decInsn.vA);
+ GEN(workBits, decInsn.vB);
+ GEN(workBits, decInsn.vC);
+ break;
+
+ case OP_FILLED_NEW_ARRAY:
+ case OP_INVOKE_VIRTUAL:
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_INTERFACE:
+ /* action <- vararg */
+ {
+ unsigned int idx;
+ for (idx = 0; idx < decInsn.vA; idx++) {
+ GEN(workBits, decInsn.arg[idx]);
+ }
+ }
+ break;
+
+ case OP_FILLED_NEW_ARRAY_RANGE:
+ case OP_INVOKE_VIRTUAL_RANGE:
+ case OP_INVOKE_SUPER_RANGE:
+ case OP_INVOKE_DIRECT_RANGE:
+ case OP_INVOKE_STATIC_RANGE:
+ case OP_INVOKE_INTERFACE_RANGE:
+ /* action <- vararg/range */
+ {
+ unsigned int idx;
+ for (idx = 0; idx < decInsn.vA; idx++) {
+ GEN(workBits, decInsn.vC + idx);
+ }
+ }
+ break;
+
+ case OP_MOVE_RESULT:
+ case OP_MOVE_RESULT_WIDE:
+ case OP_MOVE_RESULT_OBJECT:
+ case OP_MOVE_EXCEPTION:
+ case OP_CONST_4:
+ case OP_CONST_16:
+ case OP_CONST:
+ case OP_CONST_HIGH16:
+ case OP_CONST_STRING:
+ case OP_CONST_STRING_JUMBO:
+ case OP_CONST_CLASS:
+ case OP_NEW_INSTANCE:
+ case OP_SGET:
+ case OP_SGET_VOLATILE:
+ case OP_SGET_BOOLEAN:
+ case OP_SGET_BYTE:
+ case OP_SGET_CHAR:
+ case OP_SGET_SHORT:
+ case OP_SGET_OBJECT:
+ case OP_SGET_OBJECT_VOLATILE:
+ /* vA <- value */
+ KILL(workBits, decInsn.vA);
+ break;
+
+ case OP_CONST_WIDE_16:
+ case OP_CONST_WIDE_32:
+ case OP_CONST_WIDE:
+ case OP_CONST_WIDE_HIGH16:
+ case OP_SGET_WIDE:
+ case OP_SGET_WIDE_VOLATILE:
+ /* vA(wide) <- value */
+ KILLW(workBits, decInsn.vA);
+ break;
+
+ case OP_MOVE:
+ case OP_MOVE_FROM16:
+ case OP_MOVE_16:
+ case OP_MOVE_OBJECT:
+ case OP_MOVE_OBJECT_FROM16:
+ case OP_MOVE_OBJECT_16:
+ case OP_INSTANCE_OF:
+ case OP_ARRAY_LENGTH:
+ case OP_NEW_ARRAY:
+ case OP_IGET:
+ case OP_IGET_VOLATILE:
+ case OP_IGET_BOOLEAN:
+ case OP_IGET_BYTE:
+ case OP_IGET_CHAR:
+ case OP_IGET_SHORT:
+ case OP_IGET_OBJECT:
+ case OP_IGET_OBJECT_VOLATILE:
+ case OP_NEG_INT:
+ case OP_NOT_INT:
+ case OP_NEG_FLOAT:
+ case OP_INT_TO_FLOAT:
+ case OP_FLOAT_TO_INT:
+ case OP_INT_TO_BYTE:
+ case OP_INT_TO_CHAR:
+ case OP_INT_TO_SHORT:
+ case OP_ADD_INT_LIT16:
+ case OP_RSUB_INT:
+ case OP_MUL_INT_LIT16:
+ case OP_DIV_INT_LIT16:
+ case OP_REM_INT_LIT16:
+ case OP_AND_INT_LIT16:
+ case OP_OR_INT_LIT16:
+ case OP_XOR_INT_LIT16:
+ case OP_ADD_INT_LIT8:
+ case OP_RSUB_INT_LIT8:
+ case OP_MUL_INT_LIT8:
+ case OP_DIV_INT_LIT8:
+ case OP_REM_INT_LIT8:
+ case OP_SHL_INT_LIT8:
+ case OP_SHR_INT_LIT8:
+ case OP_USHR_INT_LIT8:
+ case OP_AND_INT_LIT8:
+ case OP_OR_INT_LIT8:
+ case OP_XOR_INT_LIT8:
+ /* vA <- vB */
+ KILL(workBits, decInsn.vA);
+ GEN(workBits, decInsn.vB);
+ break;
+
+ case OP_IGET_WIDE:
+ case OP_IGET_WIDE_VOLATILE:
+ case OP_INT_TO_LONG:
+ case OP_INT_TO_DOUBLE:
+ case OP_FLOAT_TO_LONG:
+ case OP_FLOAT_TO_DOUBLE:
+ /* vA(wide) <- vB */
+ KILLW(workBits, decInsn.vA);
+ GEN(workBits, decInsn.vB);
+ break;
+
+ case OP_LONG_TO_INT:
+ case OP_LONG_TO_FLOAT:
+ case OP_DOUBLE_TO_INT:
+ case OP_DOUBLE_TO_FLOAT:
+ /* vA <- vB(wide) */
+ KILL(workBits, decInsn.vA);
+ GENW(workBits, decInsn.vB);
+ break;
+
+ case OP_MOVE_WIDE:
+ case OP_MOVE_WIDE_FROM16:
+ case OP_MOVE_WIDE_16:
+ case OP_NEG_LONG:
+ case OP_NOT_LONG:
+ case OP_NEG_DOUBLE:
+ case OP_LONG_TO_DOUBLE:
+ case OP_DOUBLE_TO_LONG:
+ /* vA(wide) <- vB(wide) */
+ KILLW(workBits, decInsn.vA);
+ GENW(workBits, decInsn.vB);
+ break;
+
+ case OP_CMPL_FLOAT:
+ case OP_CMPG_FLOAT:
+ case OP_AGET:
+ case OP_AGET_BOOLEAN:
+ case OP_AGET_BYTE:
+ case OP_AGET_CHAR:
+ case OP_AGET_SHORT:
+ case OP_AGET_OBJECT:
+ case OP_ADD_INT:
+ case OP_SUB_INT:
+ case OP_MUL_INT:
+ case OP_REM_INT:
+ case OP_DIV_INT:
+ case OP_AND_INT:
+ case OP_OR_INT:
+ case OP_XOR_INT:
+ case OP_SHL_INT:
+ case OP_SHR_INT:
+ case OP_USHR_INT:
+ case OP_ADD_FLOAT:
+ case OP_SUB_FLOAT:
+ case OP_MUL_FLOAT:
+ case OP_DIV_FLOAT:
+ case OP_REM_FLOAT:
+ /* vA <- vB, vC */
+ KILL(workBits, decInsn.vA);
+ GEN(workBits, decInsn.vB);
+ GEN(workBits, decInsn.vC);
+ break;
+
+ case OP_AGET_WIDE:
+ /* vA(wide) <- vB, vC */
+ KILLW(workBits, decInsn.vA);
+ GEN(workBits, decInsn.vB);
+ GEN(workBits, decInsn.vC);
+ break;
+
+ case OP_CMPL_DOUBLE:
+ case OP_CMPG_DOUBLE:
+ case OP_CMP_LONG:
+ /* vA <- vB(wide), vC(wide) */
+ KILL(workBits, decInsn.vA);
+ GENW(workBits, decInsn.vB);
+ GENW(workBits, decInsn.vC);
+ break;
+
+ case OP_SHL_LONG:
+ case OP_SHR_LONG:
+ case OP_USHR_LONG:
+ /* vA(wide) <- vB(wide), vC */
+ KILLW(workBits, decInsn.vA);
+ GENW(workBits, decInsn.vB);
+ GEN(workBits, decInsn.vC);
+ break;
+
+ case OP_ADD_LONG:
+ case OP_SUB_LONG:
+ case OP_MUL_LONG:
+ case OP_DIV_LONG:
+ case OP_REM_LONG:
+ case OP_AND_LONG:
+ case OP_OR_LONG:
+ case OP_XOR_LONG:
+ case OP_ADD_DOUBLE:
+ case OP_SUB_DOUBLE:
+ case OP_MUL_DOUBLE:
+ case OP_DIV_DOUBLE:
+ case OP_REM_DOUBLE:
+ /* vA(wide) <- vB(wide), vC(wide) */
+ KILLW(workBits, decInsn.vA);
+ GENW(workBits, decInsn.vB);
+ GENW(workBits, decInsn.vC);
+ break;
+
+ case OP_ADD_INT_2ADDR:
+ case OP_SUB_INT_2ADDR:
+ case OP_MUL_INT_2ADDR:
+ case OP_REM_INT_2ADDR:
+ case OP_SHL_INT_2ADDR:
+ case OP_SHR_INT_2ADDR:
+ case OP_USHR_INT_2ADDR:
+ case OP_AND_INT_2ADDR:
+ case OP_OR_INT_2ADDR:
+ case OP_XOR_INT_2ADDR:
+ case OP_DIV_INT_2ADDR:
+ /* vA <- vA, vB */
+ /* KILL(workBits, decInsn.vA); */
+ GEN(workBits, decInsn.vA);
+ GEN(workBits, decInsn.vB);
+ break;
+
+ case OP_SHL_LONG_2ADDR:
+ case OP_SHR_LONG_2ADDR:
+ case OP_USHR_LONG_2ADDR:
+ /* vA(wide) <- vA(wide), vB */
+ /* KILLW(workBits, decInsn.vA); */
+ GENW(workBits, decInsn.vA);
+ GEN(workBits, decInsn.vB);
+ break;
+
+ case OP_ADD_LONG_2ADDR:
+ case OP_SUB_LONG_2ADDR:
+ case OP_MUL_LONG_2ADDR:
+ case OP_DIV_LONG_2ADDR:
+ case OP_REM_LONG_2ADDR:
+ case OP_AND_LONG_2ADDR:
+ case OP_OR_LONG_2ADDR:
+ case OP_XOR_LONG_2ADDR:
+ case OP_ADD_FLOAT_2ADDR:
+ case OP_SUB_FLOAT_2ADDR:
+ case OP_MUL_FLOAT_2ADDR:
+ case OP_DIV_FLOAT_2ADDR:
+ case OP_REM_FLOAT_2ADDR:
+ case OP_ADD_DOUBLE_2ADDR:
+ case OP_SUB_DOUBLE_2ADDR:
+ case OP_MUL_DOUBLE_2ADDR:
+ case OP_DIV_DOUBLE_2ADDR:
+ case OP_REM_DOUBLE_2ADDR:
+ /* vA(wide) <- vA(wide), vB(wide) */
+ /* KILLW(workBits, decInsn.vA); */
+ GENW(workBits, decInsn.vA);
+ GENW(workBits, decInsn.vB);
+ break;
+
+ /* we will only see this if liveness analysis is done after general vfy */
+ case OP_THROW_VERIFICATION_ERROR:
+ /* no registers used */
+ break;
+
+ /* quickened instructions, not expected to appear */
+ case OP_EXECUTE_INLINE:
+ case OP_EXECUTE_INLINE_RANGE:
+ case OP_INVOKE_DIRECT_EMPTY:
+ case OP_IGET_QUICK:
+ case OP_IGET_WIDE_QUICK:
+ case OP_IGET_OBJECT_QUICK:
+ case OP_IPUT_QUICK:
+ case OP_IPUT_WIDE_QUICK:
+ case OP_IPUT_OBJECT_QUICK:
+ case OP_INVOKE_VIRTUAL_QUICK:
+ case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+ case OP_INVOKE_SUPER_QUICK:
+ case OP_INVOKE_SUPER_QUICK_RANGE:
+ case OP_RETURN_VOID_BARRIER:
+ return false;
+
+ /* these should never appear during verification */
+ case OP_UNUSED_3E:
+ case OP_UNUSED_3F:
+ case OP_UNUSED_40:
+ case OP_UNUSED_41:
+ case OP_UNUSED_42:
+ case OP_UNUSED_43:
+ case OP_UNUSED_73:
+ case OP_UNUSED_79:
+ case OP_UNUSED_7A:
+ case OP_BREAKPOINT:
+ case OP_DISPATCH_FF:
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * This is a dexDecodeDebugInfo callback, used by markDebugLocals().
+ */
+static void markLocalsCb(void* ctxt, u2 reg, u4 startAddress, u4 endAddress,
+ const char* name, const char* descriptor, const char* signature)
+{
+ VerifierData* vdata = (VerifierData*) ctxt;
+ bool verbose = dvmWantVerboseVerification(vdata->method);
+
+ if (verbose) {
+ LOGI("%04x-%04x %2d (%s %s)\n",
+ startAddress, endAddress, reg, name, descriptor);
+ }
+
+ bool wide = (descriptor[0] == 'D' || descriptor[0] == 'J');
+ assert(reg <= vdata->insnRegCount + (wide ? 1 : 0));
+
+ /*
+ * Set the bit in all GC point instructions in the range
+ * [startAddress, endAddress).
+ */
+ unsigned int idx;
+ for (idx = startAddress; idx < endAddress; idx++) {
+ BitVector* liveRegs = vdata->registerLines[idx].liveRegs;
+ if (liveRegs != NULL) {
+ if (wide) {
+ GENW(liveRegs, reg);
+ } else {
+ GEN(liveRegs, reg);
+ }
+ }
+ }
+}
+
+/*
+ * Mark all debugger-visible locals as live.
+ *
+ * The "locals" table describes the positions of the various locals in the
+ * stack frame based on the current execution address. If the debugger
+ * wants to display one, it issues a request by "slot number". We need
+ * to ensure that references in stack slots that might be queried by the
+ * debugger aren't GCed.
+ *
+ * (If the GC had some way to mark the slot as invalid we wouldn't have
+ * to do this. We could also have the debugger interface check the
+ * register map and simply refuse to return a "dead" value, but that's
+ * potentially confusing since the referred-to object might actually be
+ * alive, and being able to see it without having to hunt around for a
+ * "live" stack frame is useful.)
+ */
+static bool markDebugLocals(VerifierData* vdata)
+{
+ const Method* meth = vdata->method;
+
+ dexDecodeDebugInfo(meth->clazz->pDvmDex->pDexFile, dvmGetMethodCode(meth),
+ meth->clazz->descriptor, meth->prototype.protoIdx, meth->accessFlags,
+ NULL, markLocalsCb, vdata);
+
+ return true;
+}
+
+
+/*
+ * Dump the liveness bits to the log.
+ *
+ * "curIdx" is for display only.
+ */
+static void dumpLiveState(const VerifierData* vdata, u4 curIdx,
+ const BitVector* workBits)
+{
+ u4 insnRegCount = vdata->insnRegCount;
+ size_t regCharSize = insnRegCount + (insnRegCount-1)/4 + 2 +1;
+ char regChars[regCharSize +1];
+ unsigned int idx;
+
+ memset(regChars, ' ', regCharSize);
+ regChars[0] = '[';
+ if (insnRegCount == 0)
+ regChars[1] = ']';
+ else
+ regChars[1 + (insnRegCount-1) + (insnRegCount-1)/4 +1] = ']';
+ regChars[regCharSize] = '\0';
+
+ for (idx = 0; idx < insnRegCount; idx++) {
+ char ch = dvmIsBitSet(workBits, idx) ? '+' : '-';
+ regChars[1 + idx + (idx/4)] = ch;
+ }
+
+ LOGI("0x%04x %s\n", curIdx, regChars);
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Liveness analysis.
+ */
+#ifndef _DALVIK_LIVENESS
+#define _DALVIK_LIVENESS
+
+struct VerifierData;
+
+bool dvmComputeLiveness(struct VerifierData* vdata);
+
+#endif /*_DALVIK_LIVENESS*/
/*
* Allocate for optimism: one slot per entry, plus an end-of-list marker.
*/
- table = calloc(count + 1, sizeof(InlineSub));
+ table = (InlineSub*)calloc(count + 1, sizeof(InlineSub));
tableIndex = 0;
for (i = 0; i < count; i++) {
*/
const RegisterMap* dvmRegisterMapGetNext(const void** pPtr)
{
- const RegisterMap* pMap = *pPtr;
+ const RegisterMap* pMap = (const RegisterMap*) *pPtr;
*pPtr = /*align32*/(((u1*) pMap) + computeRegisterMapSize(pMap));
LOGVV("getNext: %p -> %p (f=0x%x w=%d e=%d)\n",
* Get the format.
*/
INLINE RegisterMapFormat dvmRegisterMapGetFormat(const RegisterMap* pMap) {
- return pMap->format & ~(kRegMapFormatOnHeap);
+ return (RegisterMapFormat)(pMap->format & ~(kRegMapFormatOnHeap));
}
/*
/*
+ * This is used when debugging to apply a magnifying glass to the
+ * verification of a particular method.
+ */
+bool dvmWantVerboseVerification(const Method* meth)
+{
+ return false; /* COMMENT OUT to enable verbose debugging */
+
+ const char* cd = "Lcom/android/server/am/ActivityManagerService;";
+ const char* mn = "trimApplications";
+ const char* sg = "()V";
+ return (strcmp(meth->clazz->descriptor, cd) == 0 &&
+ dvmCompareNameDescriptorAndMethod(mn, sg, meth) == 0);
+}
+
+/*
* Output a code verifier warning message. For the pre-verifier it's not
* a big deal if something fails (and it may even be expected), but if
* we're doing just-in-time verification it's significant.
*
* Returns "false" on failure (e.g. this isn't a branch instruction).
*/
-bool dvmGetBranchTarget(const Method* meth, InsnFlags* insnFlags,
- int curOffset, int* pOffset, bool* pConditional)
+bool dvmGetBranchOffset(const Method* meth, const InsnFlags* insnFlags,
+ int curOffset, s4* pOffset, bool* pConditional)
{
const u2* insns = meth->insns + curOffset;
void dvmLogUnableToResolveClass(const char* missingClassDescr,
const Method* meth);
-/* extract the relative branch target from a branch instruction */
-bool dvmGetBranchTarget(const Method* meth, InsnFlags* insnFlags,
- int curOffset, int* pOffset, bool* pConditional);
+/* extract the relative branch offset from a branch instruction */
+bool dvmGetBranchOffset(const Method* meth, const InsnFlags* insnFlags,
+ int curOffset, s4* pOffset, bool* pConditional);
/* return a RegType enumeration value that "value" just fits into */
char dvmDetermineCat1Const(s4 value);
+/* debugging */
+bool dvmWantVerboseVerification(const Method* meth);
+
#endif /*_DALVIK_VERIFYSUBS*/
--- /dev/null
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Verifier basic block functions.
+ */
+#include "Dalvik.h"
+#include "analysis/VfyBasicBlock.h"
+#include "analysis/CodeVerify.h"
+#include "analysis/VerifySubs.h"
+#include "libdex/DexCatch.h"
+#include "libdex/InstrUtils.h"
+
+
+/*
+ * Extract the list of catch handlers from "pTry" into "addrBuf".
+ *
+ * Returns the size of the catch handler list. If the return value
+ * exceeds "addrBufSize", the items at the end of the list will not be
+ * represented in the output array, and this function should be called
+ * again with a larger buffer.
+ */
+static u4 extractCatchHandlers(const DexCode* pCode, const DexTry* pTry,
+ u4* addrBuf, size_t addrBufSize)
+{
+ DexCatchIterator iterator;
+ unsigned int idx = 0;
+
+ dexCatchIteratorInit(&iterator, pCode, pTry->handlerOff);
+ while (true) {
+ DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+ if (handler == NULL)
+ break;
+
+ if (idx < addrBufSize) {
+ addrBuf[idx] = handler->address;
+ }
+ idx++;
+ }
+
+ return idx;
+}
+
+/*
+ * Returns "true" if the instruction represents a data chunk, such as a
+ * switch statement block.
+ */
+static bool isDataChunk(u2 insn)
+{
+ return (insn == kPackedSwitchSignature ||
+ insn == kSparseSwitchSignature ||
+ insn == kArrayDataSignature);
+}
+
+/*
+ * Alloc a basic block in the specified slot. The storage will be
+ * initialized.
+ */
+static VfyBasicBlock* allocVfyBasicBlock(VerifierData* vdata, u4 idx)
+{
+ VfyBasicBlock* newBlock = (VfyBasicBlock*) calloc(1, sizeof(VfyBasicBlock));
+ if (newBlock == NULL)
+ return NULL;
+
+ /*
+ * TODO: there is no good default size here -- the problem is that most
+ * addresses will only have one predecessor, but a fair number will
+ * have 10+, and a few will have 100+ (e.g. the synthetic "finally"
+ * in a large synchronized method). We probably want to use a small
+ * base allocation (perhaps two) and then have the first overflow
+ * allocation jump dramatically (to 32 or thereabouts).
+ */
+ newBlock->predecessors = dvmPointerSetAlloc(32);
+ if (newBlock->predecessors == NULL) {
+ free(newBlock);
+ return NULL;
+ }
+
+ newBlock->firstAddr = (u4) -1; // DEBUG
+
+ newBlock->liveRegs = dvmAllocBitVector(vdata->insnRegCount, false);
+ if (newBlock->liveRegs == NULL) {
+ dvmPointerSetFree(newBlock->predecessors);
+ free(newBlock);
+ return NULL;
+ }
+
+ return newBlock;
+}
+
+/*
+ * Add "curBlock" to the predecessor list in "targetIdx".
+ */
+static bool addToPredecessor(VerifierData* vdata, VfyBasicBlock* curBlock,
+ u4 targetIdx)
+{
+ assert(targetIdx < vdata->insnsSize);
+
+ /*
+ * Allocate the target basic block if necessary. This will happen
+ * on e.g. forward branches.
+ *
+ * We can't fill in all the fields, but that will happen automatically
+ * when we get to that part of the code.
+ */
+ VfyBasicBlock* targetBlock = vdata->basicBlocks[targetIdx];
+ if (targetBlock == NULL) {
+ targetBlock = allocVfyBasicBlock(vdata, targetIdx);
+ if (targetBlock == NULL)
+ return false;
+ vdata->basicBlocks[targetIdx] = targetBlock;
+ }
+
+ PointerSet* preds = targetBlock->predecessors;
+ bool added = dvmPointerSetAddEntry(preds, curBlock);
+ if (!added) {
+ /*
+ * This happens sometimes for packed-switch instructions, where
+ * the same target address appears more than once. Also, a
+ * (pointless) conditional branch to the next instruction will
+ * trip over this.
+ */
+ LOGV("ODD: point set for targ=0x%04x (%p) already had block "
+ "fir=0x%04x (%p)\n",
+ targetIdx, targetBlock, curBlock->firstAddr, curBlock);
+ }
+
+ return true;
+}
+
+/*
+ * Add ourselves to the predecessor list in all blocks we might transfer
+ * control to.
+ *
+ * There are four ways to proceed to a new instruction:
+ * (1) continue to the following instruction
+ * (2) [un]conditionally branch to a specific location
+ * (3) conditionally branch through a "switch" statement
+ * (4) throw an exception
+ *
+ * Returning from the method (via a return statement or an uncaught
+ * exception) are not interesting for liveness analysis.
+ */
+static bool setPredecessors(VerifierData* vdata, VfyBasicBlock* curBlock,
+ u4 curIdx, OpcodeFlags opFlags, u4 nextIdx, u4* handlerList,
+ size_t numHandlers)
+{
+ const InsnFlags* insnFlags = vdata->insnFlags;
+ const Method* meth = vdata->method;
+
+ unsigned int handlerIdx;
+ for (handlerIdx = 0; handlerIdx < numHandlers; handlerIdx++) {
+ if (!addToPredecessor(vdata, curBlock, handlerList[handlerIdx]))
+ return false;
+ }
+
+ if ((opFlags & kInstrCanContinue) != 0) {
+ if (!addToPredecessor(vdata, curBlock, nextIdx))
+ return false;
+ }
+ if ((opFlags & kInstrCanBranch) != 0) {
+ bool unused, gotBranch;
+ s4 branchOffset, absOffset;
+
+ gotBranch = dvmGetBranchOffset(meth, insnFlags, curIdx,
+ &branchOffset, &unused);
+ assert(gotBranch);
+ absOffset = curIdx + branchOffset;
+ assert(absOffset >= 0 && (u4) absOffset < vdata->insnsSize);
+
+ if (!addToPredecessor(vdata, curBlock, absOffset))
+ return false;
+ }
+
+ if ((opFlags & kInstrCanSwitch) != 0) {
+ const u2* curInsn = &meth->insns[curIdx];
+ const u2* dataPtr;
+
+ /* these values have already been verified, so we can trust them */
+ s4 offsetToData = curInsn[1] | ((s4) curInsn[2]) << 16;
+ dataPtr = curInsn + offsetToData;
+
+ /*
+ * dataPtr points to the start of the switch data. The first
+ * item is the NOP+magic, the second is the number of entries in
+ * the switch table.
+ */
+ u2 switchCount = dataPtr[1];
+
+ /*
+ * Skip past the ident field, size field, and the first_key field
+ * (for packed) or the key list (for sparse).
+ */
+ if (dexOpcodeFromCodeUnit(meth->insns[curIdx]) == OP_PACKED_SWITCH) {
+ dataPtr += 4;
+ } else {
+ assert(dexOpcodeFromCodeUnit(meth->insns[curIdx]) ==
+ OP_SPARSE_SWITCH);
+ dataPtr += 2 + 2 * switchCount;
+ }
+
+ u4 switchIdx;
+ for (switchIdx = 0; switchIdx < switchCount; switchIdx++) {
+ s4 offset, absOffset;
+
+ offset = (s4) dataPtr[switchIdx*2] |
+ (s4) (dataPtr[switchIdx*2 +1] << 16);
+ absOffset = curIdx + offset;
+ assert(absOffset >= 0 && (u4) absOffset < vdata->insnsSize);
+
+ if (!addToPredecessor(vdata, curBlock, absOffset))
+ return false;
+ }
+ }
+
+ if (false) {
+ if (dvmPointerSetGetCount(curBlock->predecessors) > 256) {
+ LOGI("Lots of preds at 0x%04x in %s.%s:%s\n", curIdx,
+ meth->clazz->descriptor, meth->name, meth->shorty);
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Dump the contents of the basic blocks.
+ */
+static void dumpBasicBlocks(const VerifierData* vdata)
+{
+ char printBuf[256];
+ unsigned int idx;
+ int count;
+
+ LOGI("Basic blocks for %s.%s:%s\n", vdata->method->clazz->descriptor,
+ vdata->method->name, vdata->method->shorty);
+ for (idx = 0; idx < vdata->insnsSize; idx++) {
+ VfyBasicBlock* block = vdata->basicBlocks[idx];
+ if (block == NULL)
+ continue;
+
+ assert(block->firstAddr == idx);
+ count = snprintf(printBuf, sizeof(printBuf), " %04x-%04x ",
+ block->firstAddr, block->lastAddr);
+
+ PointerSet* preds = block->predecessors;
+ size_t numPreds = dvmPointerSetGetCount(preds);
+
+ if (numPreds > 0) {
+ count += snprintf(printBuf + count, sizeof(printBuf) - count,
+ "preds:");
+
+ unsigned int predIdx;
+ for (predIdx = 0; predIdx < numPreds; predIdx++) {
+ if (count >= (int) sizeof(printBuf))
+ break;
+ const VfyBasicBlock* pred =
+ (const VfyBasicBlock*) dvmPointerSetGetEntry(preds, predIdx);
+ count += snprintf(printBuf + count, sizeof(printBuf) - count,
+ "%04x(%p),", pred->firstAddr, pred);
+ }
+ } else {
+ count += snprintf(printBuf + count, sizeof(printBuf) - count,
+ "(no preds)");
+ }
+
+ printBuf[sizeof(printBuf)-2] = '!';
+ printBuf[sizeof(printBuf)-1] = '\0';
+
+ LOGI("%s", printBuf);
+ }
+
+ usleep(100 * 1000); /* ugh...let logcat catch up */
+}
+
+
+/*
+ * Generate a list of basic blocks and related information.
+ *
+ * On success, returns "true" with vdata->basicBlocks initialized.
+ */
+bool dvmComputeVfyBasicBlocks(VerifierData* vdata)
+{
+ const InsnFlags* insnFlags = vdata->insnFlags;
+ const Method* meth = vdata->method;
+ const u4 insnsSize = vdata->insnsSize;
+ const DexCode* pCode = dvmGetMethodCode(meth);
+ const DexTry* pTries = NULL;
+ const size_t kHandlerStackAllocSize = 16; /* max seen so far is 7 */
+ u4 handlerAddrs[kHandlerStackAllocSize];
+ u4* handlerListAlloc = NULL;
+ u4* handlerList = NULL;
+ size_t numHandlers = 0;
+ u4 idx, blockStartAddr;
+ bool result = false;
+
+ bool verbose = false; //dvmWantVerboseVerification(meth);
+ if (verbose) {
+ LOGI("Basic blocks for %s.%s:%s\n",
+ meth->clazz->descriptor, meth->name, meth->shorty);
+ }
+
+ /*
+ * Allocate a data structure that allows us to map from an address to
+ * the corresponding basic block. Initially all pointers are NULL.
+ * They are populated on demand as we proceed (either when we reach a
+ * new BB, or when we need to add an item to the predecessor list in
+ * a not-yet-reached BB).
+ *
+ * Only the first instruction in the block points to the BB structure;
+ * the rest remain NULL.
+ */
+ vdata->basicBlocks =
+ (VfyBasicBlock**) calloc(insnsSize, sizeof(VfyBasicBlock*));
+ if (vdata->basicBlocks == NULL)
+ goto bail;
+
+ /*
+ * The "tries" list is a series of non-overlapping regions with a list
+ * of "catch" handlers. Rather than do the "find a matching try block"
+ * computation at each step, we just walk the "try" list in parallel.
+ *
+ * Not all methods have "try" blocks. If this one does, we init tryEnd
+ * to zero, so that the (exclusive bound) range check trips immediately.
+ */
+ u4 tryIndex = 0, tryStart = 0, tryEnd = 0;
+ if (pCode->triesSize != 0) {
+ pTries = dexGetTries(pCode);
+ }
+
+ u4 debugBBIndex = 0;
+
+ /*
+ * The address associated with a basic block is the start address.
+ */
+ blockStartAddr = 0;
+
+ for (idx = 0; idx < insnsSize; ) {
+ /*
+ * Make sure we're pointing at the right "try" block. It should
+ * not be possible to "jump over" a block, so if we're no longer
+ * in the correct one we can just advance to the next.
+ */
+ if (pTries != NULL && idx >= tryEnd) {
+ if (tryIndex == pCode->triesSize) {
+ /* no more try blocks in this method */
+ pTries = NULL;
+ numHandlers = 0;
+ } else {
+ /*
+ * Extract the set of handlers. We want to avoid doing
+ * this for each block, so we copy them to local storage.
+ * If it doesn't fit in the small stack area, we'll use
+ * the heap instead.
+ *
+ * It's rare to encounter a method with more than half a
+ * dozen possible handlers.
+ */
+ tryStart = pTries[tryIndex].startAddr;
+ tryEnd = tryStart + pTries[tryIndex].insnCount;
+
+ if (handlerListAlloc != NULL) {
+ free(handlerListAlloc);
+ handlerListAlloc = NULL;
+ }
+ numHandlers = extractCatchHandlers(pCode, &pTries[tryIndex],
+ handlerAddrs, kHandlerStackAllocSize);
+ assert(numHandlers > 0); // TODO make sure this is verified
+ if (numHandlers <= kHandlerStackAllocSize) {
+ handlerList = handlerAddrs;
+ } else {
+ LOGD("overflow, numHandlers=%d\n", numHandlers);
+ handlerListAlloc = (u4*) malloc(sizeof(u4) * numHandlers);
+ if (handlerListAlloc == NULL)
+ return false;
+ extractCatchHandlers(pCode, &pTries[tryIndex],
+ handlerListAlloc, numHandlers);
+ handlerList = handlerListAlloc;
+ }
+
+ LOGV("+++ start=%x end=%x numHan=%d\n",
+ tryStart, tryEnd, numHandlers);
+
+ tryIndex++;
+ }
+ }
+
+ /*
+ * Check the current instruction, and possibly aspects of the
+ * next instruction, to see if this instruction ends the current
+ * basic block.
+ *
+ * Instructions that can throw only end the block if there is the
+ * possibility of a local handler catching the exception.
+ */
+ Opcode opcode = dexOpcodeFromCodeUnit(meth->insns[idx]);
+ OpcodeFlags opFlags = dexGetFlagsFromOpcode(opcode);
+ size_t nextIdx = idx + dexGetWidthFromInstruction(&meth->insns[idx]);
+ bool endBB = false;
+ bool ignoreInstr = false;
+
+ if ((opFlags & kInstrCanContinue) == 0) {
+ /* does not continue */
+ endBB = true;
+ } else if ((opFlags & (kInstrCanBranch | kInstrCanSwitch)) != 0) {
+ /* conditionally branches elsewhere */
+ endBB = true;
+ } else if ((opFlags & kInstrCanThrow) != 0 &&
+ dvmInsnIsInTry(insnFlags, idx))
+ {
+ /* throws an exception that might be caught locally */
+ endBB = true;
+ } else if (isDataChunk(meth->insns[idx])) {
+ /*
+ * If this is a data chunk (e.g. switch data) we want to skip
+ * over it entirely. Set endBB so we don't carry this along as
+ * the start of a block, and ignoreInstr so we don't try to
+ * open a basic block for this instruction.
+ */
+ endBB = ignoreInstr = true;
+ } else if (dvmInsnIsBranchTarget(insnFlags, nextIdx)) {
+ /*
+ * We also need to end it if the next instruction is a branch
+ * target. Note we've tagged exception catch blocks as such.
+ *
+ * If we're this far along in the "else" chain, we know that
+ * this isn't a data-chunk NOP, and control can continue to
+ * the next instruction, so we're okay examining "nextIdx".
+ */
+ assert(nextIdx < insnsSize);
+ endBB = true;
+ } else if (opcode == OP_NOP && isDataChunk(meth->insns[nextIdx])) {
+ /*
+ * Handle an odd special case: if this is NOP padding before a
+ * data chunk, also treat it as "ignore". Otherwise it'll look
+ * like a block that starts and doesn't end.
+ */
+ endBB = ignoreInstr = true;
+ } else {
+ /* check: return ops should be caught by absence of can-continue */
+ assert((opFlags & kInstrCanReturn) == 0);
+ }
+
+ if (verbose) {
+ char btc = dvmInsnIsBranchTarget(insnFlags, idx) ? '>' : ' ';
+ char tryc =
+ (pTries != NULL && idx >= tryStart && idx < tryEnd) ? 't' : ' ';
+ bool startBB = (idx == blockStartAddr);
+ const char* startEnd;
+
+
+ if (ignoreInstr)
+ startEnd = "IGNORE";
+ else if (startBB && endBB)
+ startEnd = "START/END";
+ else if (startBB)
+ startEnd = "START";
+ else if (endBB)
+ startEnd = "END";
+ else
+ startEnd = "-";
+
+ LOGI("%04x: %c%c%s #%d\n", idx, tryc, btc, startEnd, debugBBIndex);
+
+ if (pTries != NULL && idx == tryStart) {
+ assert(numHandlers > 0);
+ LOGI(" EXC block: [%04x, %04x) %d:(%04x...)\n",
+ tryStart, tryEnd, numHandlers, handlerList[0]);
+ }
+ }
+
+ if (idx != blockStartAddr) {
+ /* should not be a basic block struct associated with this addr */
+ assert(vdata->basicBlocks[idx] == NULL);
+ }
+ if (endBB) {
+ if (!ignoreInstr) {
+ /*
+ * Create a new BB if one doesn't already exist.
+ */
+ VfyBasicBlock* curBlock = vdata->basicBlocks[blockStartAddr];
+ if (curBlock == NULL) {
+ curBlock = allocVfyBasicBlock(vdata, blockStartAddr);
+ if (curBlock == NULL)
+ return false;
+ vdata->basicBlocks[blockStartAddr] = curBlock;
+ }
+
+ curBlock->firstAddr = blockStartAddr;
+ curBlock->lastAddr = idx;
+
+ if (!setPredecessors(vdata, curBlock, idx, opFlags, nextIdx,
+ handlerList, numHandlers))
+ {
+ goto bail;
+ }
+ }
+
+ blockStartAddr = nextIdx;
+ debugBBIndex++;
+ }
+
+ idx = nextIdx;
+ }
+
+ assert(idx == insnsSize);
+
+ result = true;
+
+ if (verbose)
+ dumpBasicBlocks(vdata);
+
+bail:
+ free(handlerListAlloc);
+ return result;
+}
+
+/*
+ * Free the storage used by basic blocks.
+ */
+void dvmFreeVfyBasicBlocks(VerifierData* vdata)
+{
+ unsigned int idx;
+
+ if (vdata->basicBlocks == NULL)
+ return;
+
+ for (idx = 0; idx < vdata->insnsSize; idx++) {
+ VfyBasicBlock* block = vdata->basicBlocks[idx];
+ if (block == NULL)
+ continue;
+
+ dvmPointerSetFree(block->predecessors);
+ free(block);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Basic block functions, as used by the verifier. (The names were chosen
+ * to avoid conflicts with similar structures used by the compiler.)
+ */
+#ifndef _DALVIK_VFYBASICBLOCK
+#define _DALVIK_VFYBASICBLOCK
+
+#include "PointerSet.h"
+
+struct VerifierData;
+
+
+/*
+ * Structure representing a basic block.
+ *
+ * This is used for liveness analysis, which is a reverse-flow algorithm,
+ * so we need to mantain a list of predecessors for each block.
+ *
+ * "liveRegs" indicates the set of registers that are live at the end of
+ * the basic block (after the last instruction has executed). Successor
+ * blocks will compare their results with this to see if this block needs
+ * to be re-evaluated. Note that this is not the same as the contents of
+ * the RegisterLine for the last instruction in the block (which reflects
+ * the state *before* the instruction has executed).
+ */
+typedef struct {
+ u4 firstAddr; /* address of first instruction */
+ u4 lastAddr; /* address of last instruction */
+ PointerSet* predecessors; /* set of basic blocks that can flow here */
+ BitVector* liveRegs; /* liveness for each register */
+ bool changed; /* input set has changed, must re-eval */
+ bool visited; /* block has been visited at least once */
+} VfyBasicBlock;
+
+/*
+ * Generate a list of basic blocks.
+ */
+bool dvmComputeVfyBasicBlocks(struct VerifierData* vdata);
+
+/*
+ * Free storage allocated by dvmComputeVfyBasicBlocks.
+ */
+void dvmFreeVfyBasicBlocks(struct VerifierData* vdata);
+
+#endif /*_DALVIK_VFYBASICBLOCK*/
gDvmJit.codeCacheByteUsed = templateSize;
/* Only flush the part in the code cache that is being used now */
- cacheflush((intptr_t) gDvmJit.codeCache,
- (intptr_t) gDvmJit.codeCache + templateSize, 0);
+ dvmCompilerCacheFlush((intptr_t) gDvmJit.codeCache,
+ (intptr_t) gDvmJit.codeCache + templateSize, 0);
int result = mprotect(gDvmJit.codeCache, gDvmJit.codeCacheSize,
PROTECT_CODE_CACHE_ATTRS);
saveArea = SAVEAREA_FROM_FP(fp);
if (print) {
- if (dvmIsBreakFrame(fp)) {
+ if (dvmIsBreakFrame((u4*)fp)) {
LOGD(" #%d: break frame (%p)",
stackLevel, saveArea->returnAddr);
}
memset((char *) gDvmJit.codeCache + gDvmJit.templateSize,
0,
gDvmJit.codeCacheByteUsed - gDvmJit.templateSize);
- cacheflush((intptr_t) gDvmJit.codeCache,
- (intptr_t) gDvmJit.codeCache + gDvmJit.codeCacheByteUsed, 0);
+ dvmCompilerCacheFlush((intptr_t) gDvmJit.codeCache,
+ (intptr_t) gDvmJit.codeCache +
+ gDvmJit.codeCacheByteUsed, 0);
PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
{
JitEntry *pJitTable = NULL;
unsigned char *pJitProfTable = NULL;
+ JitTraceProfCounters *pJitTraceProfCounters = NULL;
unsigned int i;
if (!dvmCompilerArchInit())
/* Is chain field wide enough for termination pattern? */
assert(pJitTable[0].u.info.chain == gDvmJit.jitTableSize);
+ /* Allocate the trace profiling structure */
+ pJitTraceProfCounters = (JitTraceProfCounters*)
+ calloc(1, sizeof(*pJitTraceProfCounters));
+ if (!pJitTraceProfCounters) {
+ LOGE("jit trace prof counters allocation failed\n");
+ dvmUnlockMutex(&gDvmJit.tableLock);
+ goto fail;
+ }
+
gDvmJit.pJitEntryTable = pJitTable;
gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
gDvmJit.jitTableEntriesUsed = 0;
*/
gDvmJit.pProfTable = dvmDebuggerOrProfilerActive() ? NULL : pJitProfTable;
gDvmJit.pProfTableCopy = pJitProfTable;
+ gDvmJit.pJitTraceProfCounters = pJitTraceProfCounters;
dvmUnlockMutex(&gDvmJit.tableLock);
/* Signal running threads to refresh their cached pJitTable pointers */
if (gDvmJit.haltCompilerThread) {
LOGD("Compiler shutdown in progress - discarding request");
} else if (!gDvmJit.codeCacheFull) {
- bool compileOK = false;
jmp_buf jmpBuf;
work.bailPtr = &jmpBuf;
bool aborted = setjmp(jmpBuf);
if (!aborted) {
- compileOK = dvmCompilerDoWork(&work);
- }
- if (aborted || !compileOK) {
- dvmCompilerArenaReset();
- } else if (!work.result.discardResult &&
- work.result.codeAddress) {
- /* Make sure that proper code addr is installed */
- assert(work.result.codeAddress != NULL);
- dvmJitSetCodeAddr(work.pc, work.result.codeAddress,
- work.result.instructionSet);
+ bool codeCompiled = dvmCompilerDoWork(&work);
+ if (codeCompiled && !work.result.discardResult &&
+ work.result.codeAddress) {
+ dvmJitSetCodeAddr(work.pc, work.result.codeAddress,
+ work.result.instructionSet,
+ work.result.profileCodeSize);
+ }
}
+ dvmCompilerArenaReset();
}
free(work.info);
#if defined(WITH_JIT_TUNING)
gDvmJit.pProfTable = NULL;
gDvmJit.pProfTableCopy = NULL;
- if (gDvm.verboseShutdown) {
+ if (gDvm.verboseShutdown ||
+ gDvmJit.profileMode == kTraceProfilingContinuous) {
dvmCompilerDumpStats();
while (gDvmJit.compilerQueueLength)
sleep(5);
#define COMPILER_TRACE_CHAINING(X)
/* Macro to change the permissions applied to a chunk of the code cache */
-#if !defined(WITH_JIT_TUNING)
#define PROTECT_CODE_CACHE_ATTRS (PROT_READ | PROT_EXEC)
#define UNPROTECT_CODE_CACHE_ATTRS (PROT_READ | PROT_EXEC | PROT_WRITE)
-#else
-/* When doing JIT profiling always grant the write permission */
-#define PROTECT_CODE_CACHE_ATTRS (PROT_READ | PROT_EXEC | \
- (gDvmJit.profile ? PROT_WRITE : 0))
-#define UNPROTECT_CODE_CACHE_ATTRS (PROT_READ | PROT_EXEC | PROT_WRITE)
-#endif
/* Acquire the lock before removing PROT_WRITE from the specified mem region */
#define UNPROTECT_CODE_CACHE(addr, size) \
typedef struct JitTranslationInfo {
void *codeAddress;
JitInstructionSetType instructionSet;
+ int profileCodeSize;
bool discardResult; // Used for debugging divergence and IC patching
bool methodCompilationAborted; // Cannot compile the whole method
Thread *requestingThread; // For debugging purpose
kWorkOrderMethod = 1, // Work is to compile a whole method
kWorkOrderTrace = 2, // Work is to compile code fragment(s)
kWorkOrderTraceDebug = 3, // Work is to compile/debug code fragment(s)
+ kWorkOrderProfileMode = 4, // Change profiling mode
} WorkOrderKind;
typedef struct CompilerWorkOrder {
#define JIT_OPT_NO_LOOP (1 << kJitOptNoLoop)
+/* Customized node traversal orders for different needs */
+typedef enum DataFlowAnalysisMode {
+ kAllNodes = 0, // All nodes
+ kReachableNodes, // All reachable nodes
+ kPreOrderDFSTraversal, // Depth-First-Search / Pre-Order
+ kPostOrderDFSTraversal, // Depth-First-Search / Post-Order
+ kPostOrderDOMTraversal, // Dominator tree / Post-Order
+} DataFlowAnalysisMode;
+
typedef struct CompilerMethodStats {
const Method *method; // Used as hash entry signature
int dalvikSize; // # of bytes for dalvik bytecodes
bool isCallee);
bool dvmCompilerCanIncludeThisInstruction(const Method *method,
const DecodedInstruction *insn);
-bool dvmCompileMethod(struct CompilationUnit *cUnit, const Method *method,
- JitTranslationInfo *info);
+bool dvmCompileMethod(const Method *method);
bool dvmCompileTrace(JitTraceDescription *trace, int numMaxInsts,
JitTranslationInfo *info, jmp_buf *bailPtr, int optHints);
void dvmCompilerDumpStats(void);
void dvmCompilerPerformSafePointChecks(void);
void dvmCompilerInlineMIR(struct CompilationUnit *cUnit);
void dvmInitializeSSAConversion(struct CompilationUnit *cUnit);
-int dvmConvertSSARegToDalvik(struct CompilationUnit *cUnit, int ssaReg);
+int dvmConvertSSARegToDalvik(const struct CompilationUnit *cUnit, int ssaReg);
bool dvmCompilerLoopOpt(struct CompilationUnit *cUnit);
void dvmCompilerNonLoopAnalysis(struct CompilationUnit *cUnit);
-void dvmCompilerFindLiveIn(struct CompilationUnit *cUnit,
- struct BasicBlock *bb);
-void dvmCompilerDoSSAConversion(struct CompilationUnit *cUnit,
+bool dvmCompilerFindLocalLiveIn(struct CompilationUnit *cUnit,
+ struct BasicBlock *bb);
+bool dvmCompilerDoSSAConversion(struct CompilationUnit *cUnit,
struct BasicBlock *bb);
-void dvmCompilerDoConstantPropagation(struct CompilationUnit *cUnit,
+bool dvmCompilerDoConstantPropagation(struct CompilationUnit *cUnit,
struct BasicBlock *bb);
-void dvmCompilerFindInductionVariables(struct CompilationUnit *cUnit,
+bool dvmCompilerFindInductionVariables(struct CompilationUnit *cUnit,
struct BasicBlock *bb);
-char *dvmCompilerGetDalvikDisassembly(DecodedInstruction *insn, char *note);
+/* Clear the visited flag for each BB */
+bool dvmCompilerClearVisitedFlag(struct CompilationUnit *cUnit,
+ struct BasicBlock *bb);
+char *dvmCompilerGetDalvikDisassembly(const DecodedInstruction *insn,
+ char *note);
+char *dvmCompilerFullDisassembler(const struct CompilationUnit *cUnit,
+ const struct MIR *mir);
char *dvmCompilerGetSSAString(struct CompilationUnit *cUnit,
struct SSARepresentation *ssaRep);
void dvmCompilerDataFlowAnalysisDispatcher(struct CompilationUnit *cUnit,
- void (*func)(struct CompilationUnit *, struct BasicBlock *));
+ bool (*func)(struct CompilationUnit *, struct BasicBlock *),
+ DataFlowAnalysisMode dfaMode,
+ bool isIterative);
+void dvmCompilerMethodSSATransformation(struct CompilationUnit *cUnit);
void dvmCompilerStateRefresh(void);
JitTraceDescription *dvmCopyTraceDescriptor(const u2 *pc,
const struct JitEntry *desc);
kMethodExitBlock,
kPCReconstruction,
kExceptionHandling,
+ kCatchEntry,
} BBType;
typedef struct ChainCellCounts {
struct BasicBlockDataFlow;
+/* For successorBlockList */
+typedef enum BlockListType {
+ kNotUsed = 0,
+ kCatch,
+ kPackedSwitch,
+ kSparseSwitch,
+} BlockListType;
+
typedef struct BasicBlock {
int id;
- int visited;
+ bool visited;
unsigned int startOffset;
const Method *containingMethod; // For blocks from the callee
BBType blockType;
MIR *lastMIRInsn;
struct BasicBlock *fallThrough;
struct BasicBlock *taken;
- struct BasicBlock *next; // Serial link for book keeping purposes
+ struct BasicBlock *iDom; // Immediate dominator
struct BasicBlockDataFlow *dataFlowInfo;
+ BitVector *predecessors;
+ BitVector *dominators;
+ BitVector *iDominated; // Set nodes being immediately dominated
+ BitVector *domFrontier; // Dominance frontier
+ struct { // For one-to-many successors like
+ BlockListType blockListType; // switch and exception handling
+ GrowableList blocks;
+ } successorBlockList;
} BasicBlock;
+/*
+ * The "blocks" field in "successorBlockList" points to an array of
+ * elements with the type "SuccessorBlockInfo".
+ * For catch blocks, key is type index for the exception.
+ * For swtich blocks, key is the case value.
+ */
+typedef struct SuccessorBlockInfo {
+ BasicBlock *block;
+ int key;
+} SuccessorBlockInfo;
+
struct LoopAnalysis;
struct RegisterPool;
typedef struct CompilationUnit {
int numInsts;
int numBlocks;
- BasicBlock **blockList;
+ GrowableList blockList;
const Method *method;
const JitTraceDescription *traceDesc;
LIR *firstLIRInsn;
void *baseAddr;
bool printMe;
bool allSingleStep;
- bool executionCount; // Add code to count trace executions
bool hasLoop; // Contains a loop
bool hasInvoke; // Contains an invoke instruction
bool heapMemOp; // Mark mem ops for self verification
bool wholeMethod;
+ int profileCodeSize; // Size of the profile prefix in bytes
int numChainingCells[kChainingCellGap];
LIR *firstChainingLIR[kChainingCellGap];
LIR *chainingCellBottom;
* MAX_CHAINED_SWITCH_CASES cases.
*/
const u2 *switchOverflowPad;
+
+ /* New fields only for method-based compilation */
+ int numReachableBlocks;
+ int numDalvikRegisters; // method->registersSize + inlined
+ BasicBlock *entryBlock;
+ BasicBlock *exitBlock;
+ GrowableList dfsOrder;
+ GrowableList domPostOrderTraversal;
+ BitVector *tryBlockAddr;
+ BitVector **defBlockMatrix; // numDalvikRegister x numBlocks
+ BitVector *tempBlockV;
+ BitVector *tempDalvikRegisterV;
+ BitVector *tempSSARegisterV; // numSSARegs
+ bool printSSANames;
} CompilationUnit;
#if defined(WITH_SELF_VERIFICATION)
#define HEAP_ACCESS_SHADOW(_state)
#endif
-BasicBlock *dvmCompilerNewBB(BBType blockType);
+BasicBlock *dvmCompilerNewBB(BBType blockType, int blockId);
void dvmCompilerAppendMIR(BasicBlock *bb, MIR *mir);
typedef struct GrowableList {
size_t numAllocated;
size_t numUsed;
- void **elemList;
+ intptr_t *elemList;
} GrowableList;
+typedef struct GrowableListIterator {
+ GrowableList *list;
+ size_t idx;
+ size_t size;
+} GrowableListIterator;
+
#define GET_ELEM_N(LIST, TYPE, N) (((TYPE*) LIST->elemList)[N])
+#define BLOCK_NAME_LEN 80
+
+/* Forward declarations */
struct LIR;
+struct BasicBlock;
void dvmInitGrowableList(GrowableList *gList, size_t initLength);
-void dvmInsertGrowableList(GrowableList *gList, void *elem);
-BitVector* dvmCompilerAllocBitVector(int startBits, bool expandable);
-bool dvmCompilerSetBit(BitVector* pBits, int num);
+void dvmInsertGrowableList(GrowableList *gList, intptr_t elem);
+void dvmGrowableListIteratorInit(GrowableList *gList,
+ GrowableListIterator *iterator);
+intptr_t dvmGrowableListIteratorNext(GrowableListIterator *iterator);
+intptr_t dvmGrowableListGetElement(const GrowableList *gList, size_t idx);
+
+BitVector* dvmCompilerAllocBitVector(unsigned int startBits, bool expandable);
+bool dvmCompilerSetBit(BitVector* pBits, unsigned int num);
+bool dvmCompilerClearBit(BitVector* pBits, unsigned int num);
+void dvmCompilerMarkAllBits(BitVector *pBits, bool set);
void dvmDebugBitVector(char *msg, const BitVector *bv, int length);
void dvmDumpLIRInsn(struct LIR *lir, unsigned char *baseAddr);
void dvmDumpResourceMask(struct LIR *lir, u8 mask, const char *prefix);
+void dvmDumpBlockBitVector(const GrowableList *blocks, char *msg,
+ const BitVector *bv, int length);
+void dvmGetBlockName(struct BasicBlock *bb, char *name);
+int dvmCompilerCacheFlush(long start, long end, long flags);
+
#endif /* _DALVIK_COMPILER_UTILITY */
};
/* Return the Dalvik register/subscript pair of a given SSA register */
-int dvmConvertSSARegToDalvik(CompilationUnit *cUnit, int ssaReg)
+int dvmConvertSSARegToDalvik(const CompilationUnit *cUnit, int ssaReg)
{
return GET_ELEM_N(cUnit->ssaToDalvikMap, int, ssaReg);
}
* and subscript pair. Each SSA register can be used to index the
* ssaToDalvikMap list to get the subscript[31..16]/dalvik_reg[15..0] mapping.
*/
-char *dvmCompilerGetDalvikDisassembly(DecodedInstruction *insn,
+char *dvmCompilerGetDalvikDisassembly(const DecodedInstruction *insn,
char *note)
{
char buffer[256];
int opcode = insn->opcode;
int dfAttributes = dvmCompilerDataFlowAttributes[opcode];
+ int flags = dexGetFlagsFromOpcode(insn->opcode);
char *ret;
buffer[0] = 0;
- strcpy(buffer, dexGetOpcodeName(opcode));
+ if (opcode >= kMirOpFirst) {
+ if (opcode == kMirOpPhi) {
+ strcpy(buffer, "PHI");
+ }
+ else {
+ sprintf(buffer, "Opcode 0x%x", opcode);
+ }
+ } else {
+ strcpy(buffer, dexGetOpcodeName(opcode));
+ }
if (note)
strcat(buffer, note);
- if (dfAttributes & DF_FORMAT_35C) {
+ /* For branches, decode the instructions to print out the branch targets */
+ if (flags & kInstrCanBranch) {
+ InstructionFormat dalvikFormat = dexGetFormatFromOpcode(insn->opcode);
+ int offset = 0;
+ switch (dalvikFormat) {
+ case kFmt21t:
+ snprintf(buffer + strlen(buffer), 256, " v%d,", insn->vA);
+ offset = (int) insn->vB;
+ break;
+ case kFmt22t:
+ snprintf(buffer + strlen(buffer), 256, " v%d, v%d,",
+ insn->vA, insn->vB);
+ offset = (int) insn->vC;
+ break;
+ case kFmt10t:
+ case kFmt20t:
+ case kFmt30t:
+ offset = (int) insn->vA;
+ break;
+ default:
+ LOGE("Unexpected branch format: %d", dalvikFormat);
+ dvmAbort();
+ break;
+ }
+ snprintf(buffer + strlen(buffer), 256, " (%c%x)",
+ offset > 0 ? '+' : '-',
+ offset > 0 ? offset : -offset);
+ } else if (dfAttributes & DF_FORMAT_35C) {
unsigned int i;
for (i = 0; i < insn->vA; i++) {
if (i != 0) strcat(buffer, ",");
if (dfAttributes & DF_B_IS_REG) {
snprintf(buffer + strlen(buffer), 256, ", v%d", insn->vB);
}
- else {
+ else if (opcode < kMirOpFirst) {
snprintf(buffer + strlen(buffer), 256, ", (#%d)", insn->vB);
}
if (dfAttributes & DF_C_IS_REG) {
snprintf(buffer + strlen(buffer), 256, ", v%d", insn->vC);
}
- else {
+ else if (opcode < kMirOpFirst) {
snprintf(buffer + strlen(buffer), 256, ", (#%d)", insn->vC);
}
}
int length = strlen(buffer) + 1;
- ret = dvmCompilerNew(length, false);
+ ret = (char *)dvmCompilerNew(length, false);
+ memcpy(ret, buffer, length);
+ return ret;
+}
+
+char *getSSAName(const CompilationUnit *cUnit, int ssaReg, char *name)
+{
+ int ssa2DalvikValue = dvmConvertSSARegToDalvik(cUnit, ssaReg);
+
+ sprintf(name, "v%d_%d",
+ DECODE_REG(ssa2DalvikValue), DECODE_SUB(ssa2DalvikValue));
+ return name;
+}
+
+/*
+ * Dalvik instruction disassembler with optional SSA printing.
+ */
+char *dvmCompilerFullDisassembler(const CompilationUnit *cUnit,
+ const MIR *mir)
+{
+ char buffer[256];
+ char operand0[256], operand1[256];
+ const DecodedInstruction *insn = &mir->dalvikInsn;
+ int opcode = insn->opcode;
+ int dfAttributes = dvmCompilerDataFlowAttributes[opcode];
+ int flags = dexGetFlagsFromOpcode(insn->opcode);
+ char *ret;
+ int length;
+
+ buffer[0] = 0;
+ if (opcode >= kMirOpFirst) {
+ if (opcode == kMirOpPhi) {
+ snprintf(buffer, 256, "PHI %s = (%s",
+ getSSAName(cUnit, mir->ssaRep->defs[0], operand0),
+ getSSAName(cUnit, mir->ssaRep->uses[0], operand1));
+ int i;
+ for (i = 1; i < mir->ssaRep->numUses; i++) {
+ snprintf(buffer + strlen(buffer), 256, ", %s",
+ getSSAName(cUnit, mir->ssaRep->uses[i], operand0));
+ }
+ snprintf(buffer + strlen(buffer), 256, ")");
+ }
+ else {
+ sprintf(buffer, "Opcode 0x%x", opcode);
+ }
+ goto done;
+ } else {
+ strcpy(buffer, dexGetOpcodeName(opcode));
+ }
+
+ /* For branches, decode the instructions to print out the branch targets */
+ if (flags & kInstrCanBranch) {
+ InstructionFormat dalvikFormat = dexGetFormatFromOpcode(insn->opcode);
+ int delta = 0;
+ switch (dalvikFormat) {
+ case kFmt21t:
+ snprintf(buffer + strlen(buffer), 256, " %s, ",
+ getSSAName(cUnit, mir->ssaRep->uses[0], operand0));
+ delta = (int) insn->vB;
+ break;
+ case kFmt22t:
+ snprintf(buffer + strlen(buffer), 256, " %s, %s, ",
+ getSSAName(cUnit, mir->ssaRep->uses[0], operand0),
+ getSSAName(cUnit, mir->ssaRep->uses[1], operand1));
+ delta = (int) insn->vC;
+ break;
+ case kFmt10t:
+ case kFmt20t:
+ case kFmt30t:
+ delta = (int) insn->vA;
+ break;
+ default:
+ LOGE("Unexpected branch format: %d", dalvikFormat);
+ dvmAbort();
+ break;
+ }
+ snprintf(buffer + strlen(buffer), 256, " %04x",
+ mir->offset + delta);
+ } else if (dfAttributes & (DF_FORMAT_35C | DF_FORMAT_3RC)) {
+ unsigned int i;
+ for (i = 0; i < insn->vA; i++) {
+ if (i != 0) strcat(buffer, ",");
+ snprintf(buffer + strlen(buffer), 256, " %s",
+ getSSAName(cUnit, mir->ssaRep->uses[i], operand0));
+ }
+ } else {
+ int udIdx;
+ if (mir->ssaRep->numDefs) {
+
+ for (udIdx = 0; udIdx < mir->ssaRep->numDefs; udIdx++) {
+ snprintf(buffer + strlen(buffer), 256, " %s",
+ getSSAName(cUnit, mir->ssaRep->defs[udIdx], operand0));
+ }
+ strcat(buffer, ",");
+ }
+ if (mir->ssaRep->numUses) {
+ /* No leading ',' for the first use */
+ snprintf(buffer + strlen(buffer), 256, " %s",
+ getSSAName(cUnit, mir->ssaRep->uses[0], operand0));
+ for (udIdx = 1; udIdx < mir->ssaRep->numUses; udIdx++) {
+ snprintf(buffer + strlen(buffer), 256, ", %s",
+ getSSAName(cUnit, mir->ssaRep->uses[udIdx], operand0));
+ }
+ }
+ if (opcode < kMirOpFirst) {
+ InstructionFormat dalvikFormat = dexGetFormatFromOpcode(opcode);
+ switch (dalvikFormat) {
+ case kFmt11n: // op vA, #+B
+ case kFmt21s: // op vAA, #+BBBB
+ case kFmt21h: // op vAA, #+BBBB00000[00000000]
+ case kFmt31i: // op vAA, #+BBBBBBBB
+ case kFmt51l: // op vAA, #+BBBBBBBBBBBBBBBB
+ snprintf(buffer + strlen(buffer), 256, " #%#x", insn->vB);
+ break;
+ case kFmt21c: // op vAA, thing@BBBB
+ case kFmt31c: // op vAA, thing@BBBBBBBB
+ snprintf(buffer + strlen(buffer), 256, " @%#x", insn->vB);
+ break;
+ case kFmt22b: // op vAA, vBB, #+CC
+ case kFmt22s: // op vA, vB, #+CCCC
+ snprintf(buffer + strlen(buffer), 256, " #%#x", insn->vC);
+ break;
+ case kFmt22c: // op vA, vB, thing@CCCC
+ case kFmt22cs: // [opt] op vA, vB, field offset CCCC
+ snprintf(buffer + strlen(buffer), 256, " @%#x", insn->vC);
+ break;
+ /* No need for special printing */
+ default:
+ break;
+ }
+ }
+ }
+
+done:
+ length = strlen(buffer) + 1;
+ ret = (char *) dvmCompilerNew(length, false);
memcpy(ret, buffer, length);
return ret;
}
}
int length = strlen(buffer) + 1;
- ret = dvmCompilerNew(length, false);
+ ret = (char *)dvmCompilerNew(length, false);
memcpy(ret, buffer, length);
return ret;
}
}
/* Mark a reg as being defined */
-static inline void handleLiveInDef(BitVector *defV, int dalvikRegId)
+static inline void handleDef(BitVector *defV, int dalvikRegId)
{
dvmCompilerSetBit(defV, dalvikRegId);
}
* Find out live-in variables for natural loops. Variables that are live-in in
* the main loop body are considered to be defined in the entry block.
*/
-void dvmCompilerFindLiveIn(CompilationUnit *cUnit, BasicBlock *bb)
+bool dvmCompilerFindLocalLiveIn(CompilationUnit *cUnit, BasicBlock *bb)
{
MIR *mir;
BitVector *useV, *defV, *liveInV;
- if (bb->blockType != kDalvikByteCode &&
- bb->blockType != kTraceEntryBlock) {
- return;
- }
+ if (bb->dataFlowInfo == NULL) return false;
useV = bb->dataFlowInfo->useV =
- dvmCompilerAllocBitVector(cUnit->method->registersSize, false);
+ dvmCompilerAllocBitVector(cUnit->numDalvikRegisters, false);
defV = bb->dataFlowInfo->defV =
- dvmCompilerAllocBitVector(cUnit->method->registersSize, false);
+ dvmCompilerAllocBitVector(cUnit->numDalvikRegisters, false);
liveInV = bb->dataFlowInfo->liveInV =
- dvmCompilerAllocBitVector(cUnit->method->registersSize, false);
+ dvmCompilerAllocBitVector(cUnit->numDalvikRegisters, false);
for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
int dfAttributes =
}
}
if (dfAttributes & DF_HAS_DEFS) {
- handleLiveInDef(defV, dInsn->vA);
+ handleDef(defV, dInsn->vA);
if (dfAttributes & DF_DA_WIDE) {
- handleLiveInDef(defV, dInsn->vA+1);
+ handleDef(defV, dInsn->vA+1);
}
}
}
+ return true;
}
/* Find out the latest SSA register for a given Dalvik register */
cUnit->dalvikToSSAMap[dalvikReg] = newD2SMapping;
int newS2DMapping = ENCODE_REG_SUB(dalvikReg, dalvikSub);
- dvmInsertGrowableList(cUnit->ssaToDalvikMap, (void *) newS2DMapping);
+ dvmInsertGrowableList(cUnit->ssaToDalvikMap, newS2DMapping);
defs[regIndex] = ssaReg;
}
int i;
mir->ssaRep->numUses = numUses;
- mir->ssaRep->uses = dvmCompilerNew(sizeof(int) * numUses, false);
+ mir->ssaRep->uses = (int *)dvmCompilerNew(sizeof(int) * numUses, false);
for (i = 0; i < numUses; i++) {
handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->arg[i], i);
int i;
mir->ssaRep->numUses = numUses;
- mir->ssaRep->uses = dvmCompilerNew(sizeof(int) * numUses, false);
+ mir->ssaRep->uses = (int *)dvmCompilerNew(sizeof(int) * numUses, false);
for (i = 0; i < numUses; i++) {
handleSSAUse(cUnit, mir->ssaRep->uses, dInsn->vC+i, i);
}
/* Entry function to convert a block into SSA representation */
-void dvmCompilerDoSSAConversion(CompilationUnit *cUnit, BasicBlock *bb)
+bool dvmCompilerDoSSAConversion(CompilationUnit *cUnit, BasicBlock *bb)
{
MIR *mir;
- if (bb->blockType != kDalvikByteCode && bb->blockType != kTraceEntryBlock) {
- return;
- }
+ if (bb->dataFlowInfo == NULL) return false;
for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
- mir->ssaRep = dvmCompilerNew(sizeof(SSARepresentation), true);
+ mir->ssaRep = (struct SSARepresentation *)
+ dvmCompilerNew(sizeof(SSARepresentation), true);
int dfAttributes =
dvmCompilerDataFlowAttributes[mir->dalvikInsn.opcode];
if (numUses) {
mir->ssaRep->numUses = numUses;
- mir->ssaRep->uses = dvmCompilerNew(sizeof(int) * numUses, false);
- mir->ssaRep->fpUse = dvmCompilerNew(sizeof(bool) * numUses, false);
+ mir->ssaRep->uses = (int *)dvmCompilerNew(sizeof(int) * numUses,
+ false);
+ mir->ssaRep->fpUse = (bool *)dvmCompilerNew(sizeof(bool) * numUses,
+ false);
}
int numDefs = 0;
if (numDefs) {
mir->ssaRep->numDefs = numDefs;
- mir->ssaRep->defs = dvmCompilerNew(sizeof(int) * numDefs, false);
- mir->ssaRep->fpDef = dvmCompilerNew(sizeof(bool) * numDefs, false);
+ mir->ssaRep->defs = (int *)dvmCompilerNew(sizeof(int) * numDefs,
+ false);
+ mir->ssaRep->fpDef = (bool *)dvmCompilerNew(sizeof(bool) * numDefs,
+ false);
}
DecodedInstruction *dInsn = &mir->dalvikInsn;
}
}
+ /*
+ * Take a snapshot of Dalvik->SSA mapping at the end of each block. The
+ * input to PHI nodes can be derived from the snapshot of all predecessor
+ * blocks.
+ */
bb->dataFlowInfo->dalvikToSSAMap =
- dvmCompilerNew(sizeof(int) * cUnit->method->registersSize, false);
+ (int *)dvmCompilerNew(sizeof(int) * cUnit->method->registersSize,
+ false);
- /* Take a snapshot of Dalvik->SSA mapping at the end of each block */
memcpy(bb->dataFlowInfo->dalvikToSSAMap, cUnit->dalvikToSSAMap,
sizeof(int) * cUnit->method->registersSize);
+ return true;
}
/* Setup a constant value for opcodes thare have the DF_SETS_CONST attribute */
cUnit->constantValues[ssaReg] = value;
}
-void dvmCompilerDoConstantPropagation(CompilationUnit *cUnit, BasicBlock *bb)
+bool dvmCompilerDoConstantPropagation(CompilationUnit *cUnit, BasicBlock *bb)
{
MIR *mir;
BitVector *isConstantV = cUnit->isConstantV;
}
}
/* TODO: implement code to handle arithmetic operations */
+ return true;
}
-void dvmCompilerFindInductionVariables(struct CompilationUnit *cUnit,
+bool dvmCompilerFindInductionVariables(struct CompilationUnit *cUnit,
struct BasicBlock *bb)
{
BitVector *isIndVarV = cUnit->loopAnalysis->isIndVarV;
if (bb->blockType != kDalvikByteCode &&
bb->blockType != kTraceEntryBlock) {
- return;
+ return false;
}
/* If the bb doesn't have a phi it cannot contain an induction variable */
if (bb->firstMIRInsn == NULL ||
bb->firstMIRInsn->dalvikInsn.opcode != kMirOpPhi) {
- return;
+ return false;
}
/* Find basic induction variable first */
}
if (deltaIsConstant) {
dvmSetBit(isIndVarV, mir->ssaRep->uses[0]);
- InductionVariableInfo *ivInfo =
+ InductionVariableInfo *ivInfo = (InductionVariableInfo *)
dvmCompilerNew(sizeof(InductionVariableInfo),
false);
ivInfo->m = 1; // always 1 to basic iv
ivInfo->c = 0; // N/A to basic iv
ivInfo->inc = deltaValue;
- dvmInsertGrowableList(ivList, (void *) ivInfo);
+ dvmInsertGrowableList(ivList, (intptr_t) ivInfo);
cUnit->loopAnalysis->numBasicIV++;
break;
}
if (cIsConstant) {
unsigned int i;
dvmSetBit(isIndVarV, mir->ssaRep->defs[0]);
- InductionVariableInfo *ivInfo =
+ InductionVariableInfo *ivInfo = (InductionVariableInfo *)
dvmCompilerNew(sizeof(InductionVariableInfo),
false);
InductionVariableInfo *ivInfoOld = NULL ;
for (i = 0; i < ivList->numUsed; i++) {
- ivInfoOld = ivList->elemList[i];
+ ivInfoOld = (InductionVariableInfo *) ivList->elemList[i];
if (ivInfoOld->ssaReg == mir->ssaRep->uses[0]) break;
}
ivInfo->m = ivInfoOld->m;
ivInfo->c = c + ivInfoOld->c;
ivInfo->inc = ivInfoOld->inc;
- dvmInsertGrowableList(ivList, (void *) ivInfo);
+ dvmInsertGrowableList(ivList, (intptr_t) ivInfo);
}
}
}
+ return true;
}
/* Setup the basic data structures for SSA conversion */
int i;
int numDalvikReg = cUnit->method->registersSize;
- cUnit->ssaToDalvikMap = dvmCompilerNew(sizeof(GrowableList), false);
+ cUnit->ssaToDalvikMap = (GrowableList *)dvmCompilerNew(sizeof(GrowableList),
+ false);
dvmInitGrowableList(cUnit->ssaToDalvikMap, numDalvikReg);
/*
* into "(0 << 16) | i"
*/
for (i = 0; i < numDalvikReg; i++) {
- dvmInsertGrowableList(cUnit->ssaToDalvikMap,
- (void *) ENCODE_REG_SUB(i, 0));
+ dvmInsertGrowableList(cUnit->ssaToDalvikMap, ENCODE_REG_SUB(i, 0));
}
/*
* while the high 16 bit is the current subscript. The original Dalvik
* register N is mapped to SSA register N with subscript 0.
*/
- cUnit->dalvikToSSAMap = dvmCompilerNew(sizeof(int) * numDalvikReg, false);
+ cUnit->dalvikToSSAMap = (int *)dvmCompilerNew(sizeof(int) * numDalvikReg,
+ false);
for (i = 0; i < numDalvikReg; i++) {
cUnit->dalvikToSSAMap[i] = i;
}
/*
* Allocate the BasicBlockDataFlow structure for the entry and code blocks
*/
- for (i = 0; i < cUnit->numBlocks; i++) {
- BasicBlock *bb = cUnit->blockList[i];
+ GrowableListIterator iterator;
+
+ dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+
+ while (true) {
+ BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+ if (bb == NULL) break;
if (bb->blockType == kDalvikByteCode ||
- bb->blockType == kTraceEntryBlock) {
- bb->dataFlowInfo = dvmCompilerNew(sizeof(BasicBlockDataFlow), true);
+ bb->blockType == kTraceEntryBlock ||
+ bb->blockType == kMethodEntryBlock ||
+ bb->blockType == kMethodExitBlock) {
+ bb->dataFlowInfo = (BasicBlockDataFlow *)
+ dvmCompilerNew(sizeof(BasicBlockDataFlow),
+ true);
}
}
}
+/* Clear the visited flag for each BB */
+bool dvmCompilerClearVisitedFlag(struct CompilationUnit *cUnit,
+ struct BasicBlock *bb)
+{
+ bb->visited = false;
+ return true;
+}
+
void dvmCompilerDataFlowAnalysisDispatcher(CompilationUnit *cUnit,
- void (*func)(CompilationUnit *, BasicBlock *))
+ bool (*func)(CompilationUnit *, BasicBlock *),
+ DataFlowAnalysisMode dfaMode,
+ bool isIterative)
{
- int i;
- for (i = 0; i < cUnit->numBlocks; i++) {
- BasicBlock *bb = cUnit->blockList[i];
- (*func)(cUnit, bb);
+ bool change = true;
+
+ while (change) {
+ change = false;
+
+ /* Scan all blocks and perform the operations specified in func */
+ if (dfaMode == kAllNodes) {
+ GrowableListIterator iterator;
+ dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
+ while (true) {
+ BasicBlock *bb =
+ (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+ if (bb == NULL) break;
+ change |= (*func)(cUnit, bb);
+ }
+ }
+ /*
+ * Scan all reachable blocks and perform the operations specified in
+ * func.
+ */
+ else if (dfaMode == kReachableNodes) {
+ int numReachableBlocks = cUnit->numReachableBlocks;
+ int idx;
+ const GrowableList *blockList = &cUnit->blockList;
+
+ for (idx = 0; idx < numReachableBlocks; idx++) {
+ int blockIdx = cUnit->dfsOrder.elemList[idx];
+ BasicBlock *bb =
+ (BasicBlock *) dvmGrowableListGetElement(blockList,
+ blockIdx);
+ change |= (*func)(cUnit, bb);
+ }
+ }
+ /*
+ * Scan all reachable blocks by the pre-order in the depth-first-search
+ * CFG and perform the operations specified in func.
+ */
+ else if (dfaMode == kPreOrderDFSTraversal) {
+ int numReachableBlocks = cUnit->numReachableBlocks;
+ int idx;
+ const GrowableList *blockList = &cUnit->blockList;
+
+ for (idx = 0; idx < numReachableBlocks; idx++) {
+ int dfsIdx = cUnit->dfsOrder.elemList[idx];
+ BasicBlock *bb =
+ (BasicBlock *) dvmGrowableListGetElement(blockList, dfsIdx);
+ change |= (*func)(cUnit, bb);
+ }
+ }
+ /*
+ * Scan all reachable blocks by the post-order in the depth-first-search
+ * CFG and perform the operations specified in func.
+ */
+ else if (dfaMode == kPostOrderDFSTraversal) {
+ int numReachableBlocks = cUnit->numReachableBlocks;
+ int idx;
+ const GrowableList *blockList = &cUnit->blockList;
+
+ for (idx = numReachableBlocks - 1; idx >= 0; idx--) {
+ int dfsIdx = cUnit->dfsOrder.elemList[idx];
+ BasicBlock *bb =
+ (BasicBlock *) dvmGrowableListGetElement(blockList, dfsIdx);
+ change |= (*func)(cUnit, bb);
+ }
+ }
+ /*
+ * Scan all reachable blocks by the post-order in the dominator tree
+ * and perform the operations specified in func.
+ */
+ else if (dfaMode == kPostOrderDOMTraversal) {
+ int numReachableBlocks = cUnit->numReachableBlocks;
+ int idx;
+ const GrowableList *blockList = &cUnit->blockList;
+
+ for (idx = 0; idx < numReachableBlocks; idx++) {
+ int domIdx = cUnit->domPostOrderTraversal.elemList[idx];
+ BasicBlock *bb =
+ (BasicBlock *) dvmGrowableListGetElement(blockList, domIdx);
+ change |= (*func)(cUnit, bb);
+ }
+ }
+ /* If isIterative is false, exit the loop after the first iteration */
+ change &= isIterative;
}
}
/* Main entry point to do SSA conversion for non-loop traces */
void dvmCompilerNonLoopAnalysis(CompilationUnit *cUnit)
{
- dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion);
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion,
+ kAllNodes,
+ false /* isIterative */);
}
#include "Dalvik.h"
#include "libdex/DexOpcodes.h"
+#include "libdex/DexCatch.h"
#include "interp/Jit.h"
#include "CompilerInternals.h"
#include "Dataflow.h"
+static inline bool contentIsInsn(const u2 *codePtr) {
+ u2 instr = *codePtr;
+ Opcode opcode = instr & 0xff;
+
+ /*
+ * Since the low 8-bit in metadata may look like OP_NOP, we need to check
+ * both the low and whole sub-word to determine whether it is code or data.
+ */
+ return (opcode != OP_NOP || instr == 0);
+}
+
/*
* Parse an instruction, return the length of the instruction
*/
static inline int parseInsn(const u2 *codePtr, DecodedInstruction *decInsn,
bool printMe)
{
- u2 instr = *codePtr;
- Opcode opcode = dexOpcodeFromCodeUnit(instr);
- int insnWidth;
-
// Don't parse instruction data
- if (opcode == OP_NOP && instr != 0) {
+ if (!contentIsInsn(codePtr)) {
return 0;
- } else {
- insnWidth = dexGetWidthFromOpcode(opcode);
- if (insnWidth < 0) {
- insnWidth = -insnWidth;
- }
}
+ u2 instr = *codePtr;
+ Opcode opcode = dexOpcodeFromCodeUnit(instr);
+
dexDecodeInstruction(codePtr, decInsn);
if (printMe) {
char *decodedString = dvmCompilerGetDalvikDisassembly(decInsn, NULL);
LOGD("%p: %#06x %s\n", codePtr, opcode, decodedString);
}
- return insnWidth;
+ return dexGetWidthFromOpcode(opcode);
}
#define UNKNOWN_TARGET 0xffffffff
/* For lookup only */
dummyMethodEntry.method = method;
- realMethodEntry = dvmHashTableLookup(gDvmJit.methodStatsTable, hashValue,
- &dummyMethodEntry,
- (HashCompareFunc) compareMethod,
- false);
+ realMethodEntry =
+ (CompilerMethodStats *) dvmHashTableLookup(gDvmJit.methodStatsTable, hashValue,
+ &dummyMethodEntry,
+ (HashCompareFunc) compareMethod,
+ false);
/* This method has never been analyzed before - create an entry */
if (realMethodEntry == NULL) {
const u2 *codePtr = dexCode->insns + curOffset;
int traceSize = 0; // # of half-words
const u2 *startCodePtr = codePtr;
- BasicBlock *startBB, *curBB, *lastBB;
+ BasicBlock *curBB, *entryCodeBB;
int numBlocks = 0;
static int compilationId;
CompilationUnit cUnit;
+ GrowableList *blockList;
#if defined(WITH_JIT_TUNING)
CompilerMethodStats *methodStats;
#endif
/* Initialize the printMe flag */
cUnit.printMe = gDvmJit.printMe;
- /* Initialize the profile flag */
- cUnit.executionCount = gDvmJit.profile;
-
/* Setup the method */
cUnit.method = desc->method;
/* Initialize the PC reconstruction list */
dvmInitGrowableList(&cUnit.pcReconstructionList, 8);
+ /* Initialize the basic block list */
+ blockList = &cUnit.blockList;
+ dvmInitGrowableList(blockList, 8);
+
/* Identify traces that we don't want to compile */
if (gDvmJit.methodTable) {
int len = strlen(desc->method->clazz->descriptor) +
strlen(desc->method->name) + 1;
- char *fullSignature = dvmCompilerNew(len, true);
+ char *fullSignature = (char *)dvmCompilerNew(len, true);
strcpy(fullSignature, desc->method->clazz->descriptor);
strcat(fullSignature, desc->method->name);
}
/* Allocate the entry block */
- lastBB = startBB = curBB = dvmCompilerNewBB(kTraceEntryBlock);
+ curBB = dvmCompilerNewBB(kTraceEntryBlock, numBlocks++);
+ dvmInsertGrowableList(blockList, (intptr_t) curBB);
curBB->startOffset = curOffset;
- curBB->id = numBlocks++;
- curBB = dvmCompilerNewBB(kDalvikByteCode);
- curBB->startOffset = curOffset;
- curBB->id = numBlocks++;
-
- /* Make the first real dalvik block the fallthrough of the entry block */
- startBB->fallThrough = curBB;
- lastBB->next = curBB;
- lastBB = curBB;
+ entryCodeBB = dvmCompilerNewBB(kDalvikByteCode, numBlocks++);
+ dvmInsertGrowableList(blockList, (intptr_t) entryCodeBB);
+ entryCodeBB->startOffset = curOffset;
+ curBB->fallThrough = entryCodeBB;
+ curBB = entryCodeBB;
if (cUnit.printMe) {
LOGD("--------\nCompiler: Building trace for %s, offset 0x%x\n",
while (1) {
MIR *insn;
int width;
- insn = dvmCompilerNew(sizeof(MIR), true);
+ insn = (MIR *)dvmCompilerNew(sizeof(MIR), true);
insn->offset = curOffset;
width = parseInsn(codePtr, &insn->dalvikInsn, cUnit.printMe);
if (flags & kInstrInvoke) {
assert(numInsts == 1);
CallsiteInfo *callsiteInfo =
- dvmCompilerNew(sizeof(CallsiteInfo), true);
- callsiteInfo->clazz = currRun[1].meta;
- callsiteInfo->method = currRun[2].meta;
+ (CallsiteInfo *)dvmCompilerNew(sizeof(CallsiteInfo), true);
+ callsiteInfo->clazz = (ClassObject *)currRun[1].meta;
+ callsiteInfo->method = (Method *)currRun[2].meta;
insn->meta.callsiteInfo = callsiteInfo;
}
break;
}
- curBB = dvmCompilerNewBB(kDalvikByteCode);
- lastBB->next = curBB;
- lastBB = curBB;
- curBB->id = numBlocks++;
+ curBB = dvmCompilerNewBB(kDalvikByteCode, numBlocks++);
+ dvmInsertGrowableList(blockList, (intptr_t) curBB);
curOffset = currRun->frag.startOffset;
numInsts = currRun->frag.numInsts;
curBB->startOffset = curOffset;
* taken/fallthrough links. Also create chaining cells for code not included
* in the trace.
*/
- for (curBB = startBB; curBB; curBB = curBB->next) {
+ size_t blockId;
+ for (blockId = 0; blockId < blockList->numUsed; blockId++) {
+ curBB = (BasicBlock *) dvmGrowableListGetElement(blockList, blockId);
MIR *lastInsn = curBB->lastMIRInsn;
+ BasicBlock *backwardCell;
/* Skip empty blocks */
if (lastInsn == NULL) {
continue;
}
/* No backward branch in the trace - start searching the next BB */
- for (searchBB = curBB->next; searchBB; searchBB = searchBB->next) {
+ size_t searchBlockId;
+ for (searchBlockId = blockId+1; searchBlockId < blockList->numUsed;
+ searchBlockId++) {
+ searchBB = (BasicBlock *) dvmGrowableListGetElement(blockList,
+ searchBlockId);
if (targetOffset == searchBB->startOffset) {
curBB->taken = searchBB;
}
if (curBB->taken == NULL &&
curBB->fallThrough == NULL &&
flags == (kInstrCanBranch | kInstrCanContinue) &&
- fallThroughOffset == startBB->startOffset &&
+ fallThroughOffset == entryCodeBB->startOffset &&
JIT_OPT_NO_LOOP != (optHints & JIT_OPT_NO_LOOP)) {
BasicBlock *loopBranch = curBB;
BasicBlock *exitBB;
if (cUnit.printMe) {
LOGD("Natural loop detected!");
}
- exitBB = dvmCompilerNewBB(kTraceExitBlock);
- lastBB->next = exitBB;
- lastBB = exitBB;
-
+ exitBB = dvmCompilerNewBB(kTraceExitBlock, numBlocks++);
+ dvmInsertGrowableList(blockList, (intptr_t) exitBB);
exitBB->startOffset = targetOffset;
- exitBB->id = numBlocks++;
exitBB->needFallThroughBranch = true;
loopBranch->taken = exitBB;
-#if defined(WITH_SELF_VERIFICATION)
- BasicBlock *backwardCell =
- dvmCompilerNewBB(kChainingCellBackwardBranch);
- lastBB->next = backwardCell;
- lastBB = backwardCell;
-
- backwardCell->startOffset = startBB->startOffset;
- backwardCell->id = numBlocks++;
+ backwardCell =
+ dvmCompilerNewBB(kChainingCellBackwardBranch, numBlocks++);
+ dvmInsertGrowableList(blockList, (intptr_t) backwardCell);
+ backwardCell->startOffset = entryCodeBB->startOffset;
loopBranch->fallThrough = backwardCell;
-#elif defined(WITH_JIT_TUNING)
- if (gDvmJit.profile) {
- BasicBlock *backwardCell =
- dvmCompilerNewBB(kChainingCellBackwardBranch);
- lastBB->next = backwardCell;
- lastBB = backwardCell;
-
- backwardCell->startOffset = startBB->startOffset;
- backwardCell->id = numBlocks++;
- loopBranch->fallThrough = backwardCell;
- } else {
- loopBranch->fallThrough = startBB->next;
- }
-#else
- loopBranch->fallThrough = startBB->next;
-#endif
/* Create the chaining cell as the fallthrough of the exit block */
- exitChainingCell = dvmCompilerNewBB(kChainingCellNormal);
- lastBB->next = exitChainingCell;
- lastBB = exitChainingCell;
-
+ exitChainingCell = dvmCompilerNewBB(kChainingCellNormal,
+ numBlocks++);
+ dvmInsertGrowableList(blockList, (intptr_t) exitChainingCell);
exitChainingCell->startOffset = targetOffset;
- exitChainingCell->id = numBlocks++;
exitBB->fallThrough = exitChainingCell;
/* One chaining cell for the first MAX_CHAINED_SWITCH_CASES cases */
for (i = 0; i < maxChains; i++) {
- BasicBlock *caseChain = dvmCompilerNewBB(kChainingCellNormal);
- lastBB->next = caseChain;
- lastBB = caseChain;
-
+ BasicBlock *caseChain = dvmCompilerNewBB(kChainingCellNormal,
+ numBlocks++);
+ dvmInsertGrowableList(blockList, (intptr_t) caseChain);
caseChain->startOffset = lastInsn->offset + targets[i];
- caseChain->id = numBlocks++;
}
/* One more chaining cell for the default case */
- BasicBlock *caseChain = dvmCompilerNewBB(kChainingCellNormal);
- lastBB->next = caseChain;
- lastBB = caseChain;
-
+ BasicBlock *caseChain = dvmCompilerNewBB(kChainingCellNormal,
+ numBlocks++);
+ dvmInsertGrowableList(blockList, (intptr_t) caseChain);
caseChain->startOffset = lastInsn->offset + lastInsn->width;
- caseChain->id = numBlocks++;
/* Fallthrough block not included in the trace */
} else if (!isUnconditionalBranch(lastInsn) &&
curBB->fallThrough == NULL) {
+ BasicBlock *fallThroughBB;
/*
* If the chaining cell is after an invoke or
* instruction that cannot change the control flow, request a hot
* chaining cell.
*/
if (isInvoke || curBB->needFallThroughBranch) {
- lastBB->next = dvmCompilerNewBB(kChainingCellHot);
+ fallThroughBB = dvmCompilerNewBB(kChainingCellHot, numBlocks++);
} else {
- lastBB->next = dvmCompilerNewBB(kChainingCellNormal);
+ fallThroughBB = dvmCompilerNewBB(kChainingCellNormal,
+ numBlocks++);
}
- lastBB = lastBB->next;
- lastBB->id = numBlocks++;
- lastBB->startOffset = fallThroughOffset;
- curBB->fallThrough = lastBB;
+ dvmInsertGrowableList(blockList, (intptr_t) fallThroughBB);
+ fallThroughBB->startOffset = fallThroughOffset;
+ curBB->fallThrough = fallThroughBB;
}
/* Target block not included in the trace */
if (curBB->taken == NULL &&
if (callee) {
/* JNI call doesn't need a chaining cell */
if (!dvmIsNativeMethod(callee)) {
- newBB = dvmCompilerNewBB(kChainingCellInvokeSingleton);
+ newBB = dvmCompilerNewBB(kChainingCellInvokeSingleton,
+ numBlocks++);
newBB->startOffset = 0;
newBB->containingMethod = callee;
}
/* Will resolve at runtime */
} else {
- newBB = dvmCompilerNewBB(kChainingCellInvokePredicted);
+ newBB = dvmCompilerNewBB(kChainingCellInvokePredicted,
+ numBlocks++);
newBB->startOffset = 0;
}
/* For unconditional branches, request a hot chaining cell */
#if !defined(WITH_SELF_VERIFICATION)
newBB = dvmCompilerNewBB(dexIsGoto(flags) ?
kChainingCellHot :
- kChainingCellNormal);
+ kChainingCellNormal,
+ numBlocks++);
newBB->startOffset = targetOffset;
#else
/* Handle branches that branch back into the block */
if (targetOffset >= curBB->firstMIRInsn->offset &&
targetOffset <= curBB->lastMIRInsn->offset) {
- newBB = dvmCompilerNewBB(kChainingCellBackwardBranch);
+ newBB = dvmCompilerNewBB(kChainingCellBackwardBranch,
+ numBlocks++);
} else {
newBB = dvmCompilerNewBB(dexIsGoto(flags) ?
kChainingCellHot :
- kChainingCellNormal);
+ kChainingCellNormal,
+ numBlocks++);
}
newBB->startOffset = targetOffset;
#endif
}
if (newBB) {
- newBB->id = numBlocks++;
curBB->taken = newBB;
- lastBB->next = newBB;
- lastBB = newBB;
+ dvmInsertGrowableList(blockList, (intptr_t) newBB);
}
}
}
/* Now create a special block to host PC reconstruction code */
- lastBB->next = dvmCompilerNewBB(kPCReconstruction);
- lastBB = lastBB->next;
- lastBB->id = numBlocks++;
+ curBB = dvmCompilerNewBB(kPCReconstruction, numBlocks++);
+ dvmInsertGrowableList(blockList, (intptr_t) curBB);
/* And one final block that publishes the PC and raise the exception */
- lastBB->next = dvmCompilerNewBB(kExceptionHandling);
- lastBB = lastBB->next;
- lastBB->id = numBlocks++;
+ curBB = dvmCompilerNewBB(kExceptionHandling, numBlocks++);
+ dvmInsertGrowableList(blockList, (intptr_t) curBB);
if (cUnit.printMe) {
- char* signature = dexProtoCopyMethodDescriptor(&desc->method->prototype);
+ char* signature =
+ dexProtoCopyMethodDescriptor(&desc->method->prototype);
LOGD("TRACEINFO (%d): 0x%08x %s%s.%s 0x%x %d of %d, %d blocks",
compilationId,
(intptr_t) desc->method->insns,
free(signature);
}
- BasicBlock **blockList;
-
cUnit.traceDesc = desc;
cUnit.numBlocks = numBlocks;
- blockList = cUnit.blockList =
- dvmCompilerNew(sizeof(BasicBlock *) * numBlocks, true);
-
- int i;
-
- for (i = 0, curBB = startBB; i < numBlocks; i++) {
- blockList[i] = curBB;
- curBB = curBB->next;
- }
- /* Make sure all blocks are added to the cUnit */
- assert(curBB == NULL);
/* Set the instruction set to use (NOTE: later components may change it) */
cUnit.instructionSet = dvmCompilerInstructionSet();
dvmCompilerInlineMIR(&cUnit);
}
+ cUnit.numDalvikRegisters = cUnit.method->registersSize;
+
/* Preparation for SSA conversion */
dvmInitializeSSAConversion(&cUnit);
dvmCompilerDumpCompilationUnit(&cUnit);
}
- /* Allocate Registers */
- dvmCompilerRegAlloc(&cUnit);
+ /* Allocate Registers using simple local allocation scheme */
+ dvmCompilerLocalRegAlloc(&cUnit);
/* Convert MIR to LIR, etc. */
dvmCompilerMIR2LIR(&cUnit);
#if defined(WITH_JIT_TUNING)
methodStats->nativeSize += cUnit.totalSize;
#endif
+
+ /* FIXME - to exercise the method parser, uncomment the following code */
+#if 0
+ bool dvmCompileMethod(const Method *method);
+ if (desc->trace[0].frag.startOffset == 0) {
+ dvmCompileMethod(desc->method);
+ }
+#endif
+
return info->codeAddress != NULL;
}
switch (insn->opcode) {
case OP_NEW_INSTANCE:
case OP_CHECK_CAST: {
- ClassObject *classPtr = (void*)
+ ClassObject *classPtr = (ClassObject *)(void*)
(method->clazz->pDvmDex->pResClasses[insn->vB]);
/* Class hasn't been initialized yet */
}
}
-/*
- * Similar to dvmCompileTrace, but the entity processed here is the whole
- * method.
- *
- * TODO: implementation will be revisited when the trace builder can provide
- * whole-method traces.
- */
-bool dvmCompileMethod(CompilationUnit *cUnit, const Method *method,
- JitTranslationInfo *info)
+/* Split an existing block from the specified code offset into two */
+static BasicBlock *splitBlock(CompilationUnit *cUnit,
+ unsigned int codeOffset,
+ BasicBlock *origBlock)
{
- const DexCode *dexCode = dvmGetMethodCode(method);
- const u2 *codePtr = dexCode->insns;
- const u2 *codeEnd = dexCode->insns + dexCode->insnsSize;
- int blockID = 0;
- unsigned int curOffset = 0;
+ MIR *insn = origBlock->firstMIRInsn;
+ while (insn) {
+ if (insn->offset == codeOffset) break;
+ insn = insn->next;
+ }
+ if (insn == NULL) {
+ LOGE("Break split failed");
+ dvmAbort();
+ }
+ BasicBlock *bottomBlock = dvmCompilerNewBB(kDalvikByteCode,
+ cUnit->numBlocks++);
+ dvmInsertGrowableList(&cUnit->blockList, (intptr_t) bottomBlock);
+
+ bottomBlock->startOffset = codeOffset;
+ bottomBlock->firstMIRInsn = insn;
+ bottomBlock->lastMIRInsn = origBlock->lastMIRInsn;
+
+ /* Handle the taken path */
+ bottomBlock->taken = origBlock->taken;
+ if (bottomBlock->taken) {
+ origBlock->taken = NULL;
+ dvmCompilerClearBit(bottomBlock->taken->predecessors, origBlock->id);
+ dvmCompilerSetBit(bottomBlock->taken->predecessors, bottomBlock->id);
+ }
- /* If we've already compiled this trace, just return success */
- if (dvmJitGetCodeAddr(codePtr) && !info->discardResult) {
- return true;
+ /* Handle the fallthrough path */
+ bottomBlock->fallThrough = origBlock->fallThrough;
+ origBlock->fallThrough = bottomBlock;
+ dvmCompilerSetBit(bottomBlock->predecessors, origBlock->id);
+ if (bottomBlock->fallThrough) {
+ dvmCompilerClearBit(bottomBlock->fallThrough->predecessors,
+ origBlock->id);
+ dvmCompilerSetBit(bottomBlock->fallThrough->predecessors,
+ bottomBlock->id);
}
- /* Doing method-based compilation */
- cUnit->wholeMethod = true;
+ /* Handle the successor list */
+ if (origBlock->successorBlockList.blockListType != kNotUsed) {
+ bottomBlock->successorBlockList = origBlock->successorBlockList;
+ origBlock->successorBlockList.blockListType = kNotUsed;
+ GrowableListIterator iterator;
+
+ dvmGrowableListIteratorInit(&bottomBlock->successorBlockList.blocks,
+ &iterator);
+ while (true) {
+ SuccessorBlockInfo *successorBlockInfo =
+ (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+ if (successorBlockInfo == NULL) break;
+ BasicBlock *bb = successorBlockInfo->block;
+ dvmCompilerClearBit(bb->predecessors, origBlock->id);
+ dvmCompilerSetBit(bb->predecessors, bottomBlock->id);
+ }
+ }
- BasicBlock *firstBlock = dvmCompilerNewBB(kDalvikByteCode);
- firstBlock->id = blockID++;
+ origBlock->lastMIRInsn = insn->prev;
- /* Allocate the bit-vector to track the beginning of basic blocks */
- BitVector *bbStartAddr = dvmCompilerAllocBitVector(dexCode->insnsSize+1,
- false);
- dvmCompilerSetBit(bbStartAddr, 0);
+ insn->prev->next = NULL;
+ insn->prev = NULL;
+ return bottomBlock;
+}
- int numInvokeTargets = 0;
+/*
+ * Given a code offset, find out the block that starts with it. If the offset
+ * is in the middle of an existing block, split it into two.
+ */
+static BasicBlock *findBlock(CompilationUnit *cUnit,
+ unsigned int codeOffset,
+ bool split, bool create)
+{
+ GrowableList *blockList = &cUnit->blockList;
+ BasicBlock *bb;
+ unsigned int i;
+
+ for (i = 0; i < blockList->numUsed; i++) {
+ bb = (BasicBlock *) blockList->elemList[i];
+ if (bb->blockType != kDalvikByteCode) continue;
+ if (bb->startOffset == codeOffset) return bb;
+ /* Check if a branch jumps into the middle of an existing block */
+ if ((split == true) && (codeOffset > bb->startOffset) &&
+ (bb->lastMIRInsn != NULL) &&
+ (codeOffset <= bb->lastMIRInsn->offset)) {
+ BasicBlock *newBB = splitBlock(cUnit, codeOffset, bb);
+ return newBB;
+ }
+ }
+ if (create) {
+ bb = dvmCompilerNewBB(kDalvikByteCode, cUnit->numBlocks++);
+ dvmInsertGrowableList(&cUnit->blockList, (intptr_t) bb);
+ bb->startOffset = codeOffset;
+ return bb;
+ }
+ return NULL;
+}
+
+/* Dump the CFG into a DOT graph */
+void dumpCFG(CompilationUnit *cUnit, const char *dirPrefix)
+{
+ const Method *method = cUnit->method;
+ FILE *file;
+ char* signature = dexProtoCopyMethodDescriptor(&method->prototype);
+ char *fileName = (char *) dvmCompilerNew(
+ strlen(dirPrefix) +
+ strlen(method->clazz->descriptor) +
+ strlen(method->name) +
+ strlen(signature) +
+ strlen(".dot") + 1, true);
+ sprintf(fileName, "%s%s%s%s.dot", dirPrefix,
+ method->clazz->descriptor, method->name, signature);
+ free(signature);
/*
- * Sequentially go through every instruction first and put them in a single
- * basic block. Identify block boundaries at the mean time.
+ * Convert the special characters into a filesystem- and shell-friendly
+ * format.
*/
- while (codePtr < codeEnd) {
- MIR *insn = dvmCompilerNew(sizeof(MIR), true);
- insn->offset = curOffset;
- int width = parseInsn(codePtr, &insn->dalvikInsn, false);
- bool isInvoke = false;
- const Method *callee;
- insn->width = width;
+ int i;
+ for (i = strlen(dirPrefix); fileName[i]; i++) {
+ if (fileName[i] == '/') {
+ fileName[i] = '_';
+ } else if (fileName[i] == ';') {
+ fileName[i] = '#';
+ } else if (fileName[i] == '$') {
+ fileName[i] = '+';
+ } else if (fileName[i] == '(' || fileName[i] == ')') {
+ fileName[i] = '@';
+ } else if (fileName[i] == '<' || fileName[i] == '>') {
+ fileName[i] = '=';
+ }
+ }
+ file = fopen(fileName, "w");
+ if (file == NULL) {
+ return;
+ }
+ fprintf(file, "digraph G {\n");
+
+ fprintf(file, " rankdir=TB\n");
+
+ int numReachableBlocks = cUnit->numReachableBlocks;
+ int idx;
+ const GrowableList *blockList = &cUnit->blockList;
+
+ for (idx = 0; idx < numReachableBlocks; idx++) {
+ int blockIdx = cUnit->dfsOrder.elemList[idx];
+ BasicBlock *bb = (BasicBlock *) dvmGrowableListGetElement(blockList,
+ blockIdx);
+ if (bb == NULL) break;
+ if (bb->blockType == kMethodEntryBlock) {
+ fprintf(file, " entry [shape=Mdiamond];\n");
+ } else if (bb->blockType == kMethodExitBlock) {
+ fprintf(file, " exit [shape=Mdiamond];\n");
+ } else if (bb->blockType == kDalvikByteCode) {
+ fprintf(file, " block%04x [shape=record,label = \"{ \\\n",
+ bb->startOffset);
+ const MIR *mir;
+ for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+ fprintf(file, " {%04x %s\\l}%s\\\n", mir->offset,
+ dvmCompilerFullDisassembler(cUnit, mir),
+ mir->next ? " | " : " ");
+ }
+ fprintf(file, " }\"];\n\n");
+ } else if (bb->blockType == kExceptionHandling) {
+ char blockName[BLOCK_NAME_LEN];
- /* Terminate when the data section is seen */
- if (width == 0)
- break;
+ dvmGetBlockName(bb, blockName);
+ fprintf(file, " %s [shape=invhouse];\n", blockName);
+ }
- if (!dvmCompilerCanIncludeThisInstruction(cUnit->method,
- &insn->dalvikInsn)) {
- return false;
+ char blockName1[BLOCK_NAME_LEN], blockName2[BLOCK_NAME_LEN];
+
+ if (bb->taken) {
+ dvmGetBlockName(bb, blockName1);
+ dvmGetBlockName(bb->taken, blockName2);
+ fprintf(file, " %s:s -> %s:n [style=dotted]\n",
+ blockName1, blockName2);
+ }
+ if (bb->fallThrough) {
+ dvmGetBlockName(bb, blockName1);
+ dvmGetBlockName(bb->fallThrough, blockName2);
+ fprintf(file, " %s:s -> %s:n\n", blockName1, blockName2);
}
- dvmCompilerAppendMIR(firstBlock, insn);
- /*
- * Check whether this is a block ending instruction and whether it
- * suggests the start of a new block
- */
- unsigned int target = curOffset;
+ if (bb->successorBlockList.blockListType != kNotUsed) {
+ fprintf(file, " succ%04x [shape=%s,label = \"{ \\\n",
+ bb->startOffset,
+ (bb->successorBlockList.blockListType == kCatch) ?
+ "Mrecord" : "record");
+ GrowableListIterator iterator;
+ dvmGrowableListIteratorInit(&bb->successorBlockList.blocks,
+ &iterator);
+ SuccessorBlockInfo *successorBlockInfo =
+ (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+
+ int succId = 0;
+ while (true) {
+ if (successorBlockInfo == NULL) break;
+
+ BasicBlock *destBlock = successorBlockInfo->block;
+ SuccessorBlockInfo *nextSuccessorBlockInfo =
+ (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+
+ fprintf(file, " {<f%d> %04x: %04x\\l}%s\\\n",
+ succId++,
+ successorBlockInfo->key,
+ destBlock->startOffset,
+ (nextSuccessorBlockInfo != NULL) ? " | " : " ");
+
+ successorBlockInfo = nextSuccessorBlockInfo;
+ }
+ fprintf(file, " }\"];\n\n");
+
+ dvmGetBlockName(bb, blockName1);
+ fprintf(file, " %s:s -> succ%04x:n [style=dashed]\n",
+ blockName1, bb->startOffset);
+
+ if (bb->successorBlockList.blockListType == kPackedSwitch ||
+ bb->successorBlockList.blockListType == kSparseSwitch) {
+
+ dvmGrowableListIteratorInit(&bb->successorBlockList.blocks,
+ &iterator);
+
+ succId = 0;
+ while (true) {
+ SuccessorBlockInfo *successorBlockInfo =
+ (SuccessorBlockInfo *)
+ dvmGrowableListIteratorNext(&iterator);
+ if (successorBlockInfo == NULL) break;
+
+ BasicBlock *destBlock = successorBlockInfo->block;
+
+ dvmGetBlockName(destBlock, blockName2);
+ fprintf(file, " succ%04x:f%d:e -> %s:n\n",
+ bb->startOffset, succId++,
+ blockName2);
+ }
+ }
+ }
+ fprintf(file, "\n");
/*
- * If findBlockBoundary returns true, it means the current instruction
- * is terminating the current block. If it is a branch, the target
- * address will be recorded in target.
+ * If we need to debug the dominator tree, uncomment the following code
*/
- if (findBlockBoundary(method, insn, curOffset, &target, &isInvoke,
- &callee)) {
- dvmCompilerSetBit(bbStartAddr, curOffset + width);
- /* Each invoke needs a chaining cell block */
- if (isInvoke) {
- numInvokeTargets++;
- }
- /* A branch will end the current block */
- else if (target != curOffset && target != UNKNOWN_TARGET) {
- dvmCompilerSetBit(bbStartAddr, target);
- }
+#if 0
+ dvmGetBlockName(bb, blockName1);
+ fprintf(file, " cfg%s [label=\"%s\", shape=none];\n",
+ blockName1, blockName1);
+ if (bb->iDom) {
+ dvmGetBlockName(bb->iDom, blockName2);
+ fprintf(file, " cfg%s:s -> cfg%s:n\n\n",
+ blockName2, blockName1);
}
+#endif
+ }
+ fprintf(file, "}\n");
+ fclose(file);
+}
- codePtr += width;
- /* each bit represents 16-bit quantity */
- curOffset += width;
+/* Verify if all the successor is connected with all the claimed predecessors */
+static bool verifyPredInfo(CompilationUnit *cUnit, BasicBlock *bb)
+{
+ BitVectorIterator bvIterator;
+
+ dvmBitVectorIteratorInit(bb->predecessors, &bvIterator);
+ while (true) {
+ int blockIdx = dvmBitVectorIteratorNext(&bvIterator);
+ if (blockIdx == -1) break;
+ BasicBlock *predBB = (BasicBlock *)
+ dvmGrowableListGetElement(&cUnit->blockList, blockIdx);
+ bool found = false;
+ if (predBB->taken == bb) {
+ found = true;
+ } else if (predBB->fallThrough == bb) {
+ found = true;
+ } else if (predBB->successorBlockList.blockListType != kNotUsed) {
+ GrowableListIterator iterator;
+ dvmGrowableListIteratorInit(&predBB->successorBlockList.blocks,
+ &iterator);
+ while (true) {
+ SuccessorBlockInfo *successorBlockInfo =
+ (SuccessorBlockInfo *)
+ dvmGrowableListIteratorNext(&iterator);
+ if (successorBlockInfo == NULL) break;
+ BasicBlock *succBB = successorBlockInfo->block;
+ if (succBB == bb) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (found == false) {
+ char blockName1[BLOCK_NAME_LEN], blockName2[BLOCK_NAME_LEN];
+ dvmGetBlockName(bb, blockName1);
+ dvmGetBlockName(predBB, blockName2);
+ dumpCFG(cUnit, "/data/tombstones/");
+ LOGE("Successor %s not found from %s",
+ blockName1, blockName2);
+ dvmAbort();
+ }
}
+ return true;
+}
- /*
- * The number of blocks will be equal to the number of bits set to 1 in the
- * bit vector minus 1, because the bit representing the location after the
- * last instruction is set to one.
- *
- * We also add additional blocks for invoke chaining and the number is
- * denoted by numInvokeTargets.
- */
- int numBlocks = dvmCountSetBits(bbStartAddr);
- if (dvmIsBitSet(bbStartAddr, dexCode->insnsSize)) {
- numBlocks--;
+/* Identify code range in try blocks and set up the empty catch blocks */
+static void processTryCatchBlocks(CompilationUnit *cUnit)
+{
+ const Method *meth = cUnit->method;
+ const DexCode *pCode = dvmGetMethodCode(meth);
+ int triesSize = pCode->triesSize;
+ int i;
+ int offset;
+
+ if (triesSize == 0) {
+ return;
}
- BasicBlock **blockList;
- blockList = cUnit->blockList =
- dvmCompilerNew(sizeof(BasicBlock *) * (numBlocks + numInvokeTargets),
- true);
+ const DexTry *pTries = dexGetTries(pCode);
+ BitVector *tryBlockAddr = cUnit->tryBlockAddr;
- /*
- * Register the first block onto the list and start splitting it into
- * sub-blocks.
- */
- blockList[0] = firstBlock;
- cUnit->numBlocks = 1;
+ /* Mark all the insn offsets in Try blocks */
+ for (i = 0; i < triesSize; i++) {
+ const DexTry* pTry = &pTries[i];
+ /* all in 16-bit units */
+ int startOffset = pTry->startAddr;
+ int endOffset = startOffset + pTry->insnCount;
- int i;
- for (i = 0; i < numBlocks; i++) {
- MIR *insn;
- BasicBlock *curBB = blockList[i];
- curOffset = curBB->lastMIRInsn->offset;
-
- for (insn = curBB->firstMIRInsn->next; insn; insn = insn->next) {
- /* Found the beginning of a new block, see if it is created yet */
- if (dvmIsBitSet(bbStartAddr, insn->offset)) {
- int j;
- for (j = 0; j < cUnit->numBlocks; j++) {
- if (blockList[j]->firstMIRInsn->offset == insn->offset)
- break;
- }
+ for (offset = startOffset; offset < endOffset; offset++) {
+ dvmCompilerSetBit(tryBlockAddr, offset);
+ }
+ }
- /* Block not split yet - do it now */
- if (j == cUnit->numBlocks) {
- BasicBlock *newBB = dvmCompilerNewBB(kDalvikByteCode);
- newBB->id = blockID++;
- newBB->firstMIRInsn = insn;
- newBB->startOffset = insn->offset;
- newBB->lastMIRInsn = curBB->lastMIRInsn;
- curBB->lastMIRInsn = insn->prev;
- insn->prev->next = NULL;
- insn->prev = NULL;
-
- /*
- * If the insn is not an unconditional branch, set up the
- * fallthrough link.
- */
- if (!isUnconditionalBranch(curBB->lastMIRInsn)) {
- curBB->fallThrough = newBB;
- }
+ /* Iterate over each of the handlers to enqueue the empty Catch blocks */
+ offset = dexGetFirstHandlerOffset(pCode);
+ int handlersSize = dexGetHandlersSize(pCode);
- /*
- * Fallthrough block of an invoke instruction needs to be
- * aligned to 4-byte boundary (alignment instruction to be
- * inserted later.
- */
- if (dexGetFlagsFromOpcode(curBB->lastMIRInsn->dalvikInsn.opcode)
- & kInstrInvoke) {
- newBB->isFallThroughFromInvoke = true;
- }
+ for (i = 0; i < handlersSize; i++) {
+ DexCatchIterator iterator;
+ dexCatchIteratorInit(&iterator, pCode, offset);
- /* enqueue the new block */
- blockList[cUnit->numBlocks++] = newBB;
- break;
- }
+ for (;;) {
+ DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+ if (handler == NULL) {
+ break;
}
+
+ /*
+ * Create dummy catch blocks first. Since these are created before
+ * other blocks are processed, "split" is specified as false.
+ */
+ findBlock(cUnit, handler->address,
+ /* split */
+ false,
+ /* create */
+ true);
}
+
+ offset = dexCatchIteratorGetEndOffset(&iterator, pCode);
}
+}
- if (numBlocks != cUnit->numBlocks) {
- LOGE("Expect %d vs %d basic blocks\n", numBlocks, cUnit->numBlocks);
- dvmCompilerAbort(cUnit);
+/* Process instructions with the kInstrCanBranch flag */
+static void processCanBranch(CompilationUnit *cUnit, BasicBlock *curBlock,
+ MIR *insn, int curOffset, int width, int flags,
+ const u2* codePtr, const u2* codeEnd)
+{
+ int target = curOffset;
+ switch (insn->dalvikInsn.opcode) {
+ case OP_GOTO:
+ case OP_GOTO_16:
+ case OP_GOTO_32:
+ target += (int) insn->dalvikInsn.vA;
+ break;
+ case OP_IF_EQ:
+ case OP_IF_NE:
+ case OP_IF_LT:
+ case OP_IF_GE:
+ case OP_IF_GT:
+ case OP_IF_LE:
+ target += (int) insn->dalvikInsn.vC;
+ break;
+ case OP_IF_EQZ:
+ case OP_IF_NEZ:
+ case OP_IF_LTZ:
+ case OP_IF_GEZ:
+ case OP_IF_GTZ:
+ case OP_IF_LEZ:
+ target += (int) insn->dalvikInsn.vB;
+ break;
+ default:
+ LOGE("Unexpected opcode(%d) with kInstrCanBranch set",
+ insn->dalvikInsn.opcode);
+ dvmAbort();
}
+ BasicBlock *takenBlock = findBlock(cUnit, target,
+ /* split */
+ true,
+ /* create */
+ true);
+ curBlock->taken = takenBlock;
+ dvmCompilerSetBit(takenBlock->predecessors, curBlock->id);
+
+ /* Always terminate the current block for conditional branches */
+ if (flags & kInstrCanContinue) {
+ BasicBlock *fallthroughBlock = findBlock(cUnit,
+ curOffset + width,
+ /* split */
+ false,
+ /* create */
+ true);
+ curBlock->fallThrough = fallthroughBlock;
+ dvmCompilerSetBit(fallthroughBlock->predecessors, curBlock->id);
+ } else if (codePtr < codeEnd) {
+ /* Create a fallthrough block for real instructions (incl. OP_NOP) */
+ if (contentIsInsn(codePtr)) {
+ findBlock(cUnit, curOffset + width,
+ /* split */
+ false,
+ /* create */
+ true);
+ }
+ }
+}
- /* Connect the basic blocks through the taken links */
- for (i = 0; i < numBlocks; i++) {
- BasicBlock *curBB = blockList[i];
- MIR *insn = curBB->lastMIRInsn;
- unsigned int target = insn->offset;
- bool isInvoke = false;
- const Method *callee = NULL;
+/* Process instructions with the kInstrCanSwitch flag */
+static void processCanSwitch(CompilationUnit *cUnit, BasicBlock *curBlock,
+ MIR *insn, int curOffset, int width, int flags)
+{
+ u2 *switchData= (u2 *) (cUnit->method->insns + curOffset +
+ insn->dalvikInsn.vB);
+ int size;
+ int *keyTable;
+ int *targetTable;
+ int i;
+ int firstKey;
- findBlockBoundary(method, insn, target, &target, &isInvoke, &callee);
+ /*
+ * Packed switch data format:
+ * ushort ident = 0x0100 magic value
+ * ushort size number of entries in the table
+ * int first_key first (and lowest) switch case value
+ * int targets[size] branch targets, relative to switch opcode
+ *
+ * Total size is (4+size*2) 16-bit code units.
+ */
+ if (insn->dalvikInsn.opcode == OP_PACKED_SWITCH) {
+ assert(switchData[0] == kPackedSwitchSignature);
+ size = switchData[1];
+ firstKey = switchData[2] | (switchData[3] << 16);
+ targetTable = (int *) &switchData[4];
+ keyTable = NULL; // Make the compiler happy
+ /*
+ * Sparse switch data format:
+ * ushort ident = 0x0200 magic value
+ * ushort size number of entries in the table; > 0
+ * int keys[size] keys, sorted low-to-high; 32-bit aligned
+ * int targets[size] branch targets, relative to switch opcode
+ *
+ * Total size is (2+size*4) 16-bit code units.
+ */
+ } else {
+ assert(switchData[0] == kSparseSwitchSignature);
+ size = switchData[1];
+ keyTable = (int *) &switchData[2];
+ targetTable = (int *) &switchData[2 + size*2];
+ firstKey = 0; // To make the compiler happy
+ }
+
+ if (curBlock->successorBlockList.blockListType != kNotUsed) {
+ LOGE("Successor block list already in use: %d",
+ curBlock->successorBlockList.blockListType);
+ dvmAbort();
+ }
+ curBlock->successorBlockList.blockListType =
+ (insn->dalvikInsn.opcode == OP_PACKED_SWITCH) ?
+ kPackedSwitch : kSparseSwitch;
+ dvmInitGrowableList(&curBlock->successorBlockList.blocks, size);
+
+ for (i = 0; i < size; i++) {
+ BasicBlock *caseBlock = findBlock(cUnit, curOffset + targetTable[i],
+ /* split */
+ true,
+ /* create */
+ true);
+ SuccessorBlockInfo *successorBlockInfo =
+ (SuccessorBlockInfo *) dvmCompilerNew(sizeof(SuccessorBlockInfo),
+ false);
+ successorBlockInfo->block = caseBlock;
+ successorBlockInfo->key = (insn->dalvikInsn.opcode == OP_PACKED_SWITCH)?
+ firstKey + i : keyTable[i];
+ dvmInsertGrowableList(&curBlock->successorBlockList.blocks,
+ (intptr_t) successorBlockInfo);
+ dvmCompilerSetBit(caseBlock->predecessors, curBlock->id);
+ }
+
+ /* Fall-through case */
+ BasicBlock *fallthroughBlock = findBlock(cUnit,
+ curOffset + width,
+ /* split */
+ false,
+ /* create */
+ true);
+ curBlock->fallThrough = fallthroughBlock;
+ dvmCompilerSetBit(fallthroughBlock->predecessors, curBlock->id);
+}
+
+/* Process instructions with the kInstrCanThrow flag */
+static void processCanThrow(CompilationUnit *cUnit, BasicBlock *curBlock,
+ MIR *insn, int curOffset, int width, int flags,
+ BitVector *tryBlockAddr, const u2 *codePtr,
+ const u2* codeEnd)
+{
+ const Method *method = cUnit->method;
+ const DexCode *dexCode = dvmGetMethodCode(method);
+
+ /* In try block */
+ if (dvmIsBitSet(tryBlockAddr, curOffset)) {
+ DexCatchIterator iterator;
+
+ if (!dexFindCatchHandler(&iterator, dexCode, curOffset)) {
+ LOGE("Catch block not found in dexfile for insn %x in %s",
+ curOffset, method->name);
+ dvmAbort();
- /* Found a block ended on a branch (not invoke) */
- if (isInvoke == false && target != insn->offset) {
- int j;
- /* Forward branch */
- if (target > insn->offset) {
- j = i + 1;
- } else {
- /* Backward branch */
- j = 0;
- }
- for (; j < numBlocks; j++) {
- if (blockList[j]->firstMIRInsn->offset == target) {
- curBB->taken = blockList[j];
- break;
- }
- }
}
+ if (curBlock->successorBlockList.blockListType != kNotUsed) {
+ LOGE("Successor block list already in use: %d",
+ curBlock->successorBlockList.blockListType);
+ dvmAbort();
+ }
+ curBlock->successorBlockList.blockListType = kCatch;
+ dvmInitGrowableList(&curBlock->successorBlockList.blocks, 2);
- if (isInvoke) {
- BasicBlock *newBB;
- /* Monomorphic callee */
- if (callee) {
- newBB = dvmCompilerNewBB(kChainingCellInvokeSingleton);
- newBB->startOffset = 0;
- newBB->containingMethod = callee;
- /* Will resolve at runtime */
- } else {
- newBB = dvmCompilerNewBB(kChainingCellInvokePredicted);
- newBB->startOffset = 0;
+ for (;;) {
+ DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+ if (handler == NULL) {
+ break;
}
- newBB->id = blockID++;
- curBB->taken = newBB;
- /* enqueue the new block */
- blockList[cUnit->numBlocks++] = newBB;
+
+ BasicBlock *catchBlock = findBlock(cUnit, handler->address,
+ /* split */
+ false,
+ /* create */
+ false);
+
+ SuccessorBlockInfo *successorBlockInfo =
+ (SuccessorBlockInfo *) dvmCompilerNew(sizeof(SuccessorBlockInfo),
+ false);
+ successorBlockInfo->block = catchBlock;
+ successorBlockInfo->key = handler->typeIdx;
+ dvmInsertGrowableList(&curBlock->successorBlockList.blocks,
+ (intptr_t) successorBlockInfo);
+ dvmCompilerSetBit(catchBlock->predecessors, curBlock->id);
}
+ } else {
+ BasicBlock *ehBlock = dvmCompilerNewBB(kExceptionHandling,
+ cUnit->numBlocks++);
+ curBlock->taken = ehBlock;
+ dvmInsertGrowableList(&cUnit->blockList, (intptr_t) ehBlock);
+ ehBlock->startOffset = curOffset;
+ dvmCompilerSetBit(ehBlock->predecessors, curBlock->id);
}
- if (cUnit->numBlocks != numBlocks + numInvokeTargets) {
- LOGE("Expect %d vs %d total blocks\n", numBlocks + numInvokeTargets,
- cUnit->numBlocks);
- dvmCompilerDumpCompilationUnit(cUnit);
- dvmCompilerAbort(cUnit);
+ /*
+ * Force the current block to terminate.
+ *
+ * Data may be present before codeEnd, so we need to parse it to know
+ * whether it is code or data.
+ */
+ if (codePtr < codeEnd) {
+ /* Create a fallthrough block for real instructions (incl. OP_NOP) */
+ if (contentIsInsn(codePtr)) {
+ BasicBlock *fallthroughBlock = findBlock(cUnit,
+ curOffset + width,
+ /* split */
+ false,
+ /* create */
+ true);
+ /*
+ * OP_THROW and OP_THROW_VERIFICATION_ERROR are unconditional
+ * branches.
+ */
+ if (insn->dalvikInsn.opcode != OP_THROW_VERIFICATION_ERROR &&
+ insn->dalvikInsn.opcode != OP_THROW) {
+ curBlock->fallThrough = fallthroughBlock;
+ dvmCompilerSetBit(fallthroughBlock->predecessors, curBlock->id);
+ }
+ }
}
+}
- /* Set the instruction set to use (NOTE: later components may change it) */
- cUnit->instructionSet = dvmCompilerInstructionSet();
+/*
+ * Similar to dvmCompileTrace, but the entity processed here is the whole
+ * method.
+ *
+ * TODO: implementation will be revisited when the trace builder can provide
+ * whole-method traces.
+ */
+bool dvmCompileMethod(const Method *method)
+{
+ CompilationUnit cUnit;
+ const DexCode *dexCode = dvmGetMethodCode(method);
+ const u2 *codePtr = dexCode->insns;
+ const u2 *codeEnd = dexCode->insns + dexCode->insnsSize;
+ int numBlocks = 0;
+ unsigned int curOffset = 0;
- /* Preparation for SSA conversion */
- dvmInitializeSSAConversion(cUnit);
+ memset(&cUnit, 0, sizeof(cUnit));
+ cUnit.method = method;
- /* SSA analysis */
- dvmCompilerNonLoopAnalysis(cUnit);
+ /* Initialize the block list */
+ dvmInitGrowableList(&cUnit.blockList, 4);
- /* Needs to happen after SSA naming */
- dvmCompilerInitializeRegAlloc(cUnit);
+ /* Allocate the bit-vector to track the beginning of basic blocks */
+ BitVector *tryBlockAddr = dvmCompilerAllocBitVector(dexCode->insnsSize,
+ true /* expandable */);
+ cUnit.tryBlockAddr = tryBlockAddr;
- /* Allocate Registers */
- dvmCompilerRegAlloc(cUnit);
+ /* Create the default entry and exit blocks and enter them to the list */
+ BasicBlock *entryBlock = dvmCompilerNewBB(kMethodEntryBlock, numBlocks++);
+ BasicBlock *exitBlock = dvmCompilerNewBB(kMethodExitBlock, numBlocks++);
- /* Convert MIR to LIR, etc. */
- dvmCompilerMIR2LIR(cUnit);
+ cUnit.entryBlock = entryBlock;
+ cUnit.exitBlock = exitBlock;
+
+ dvmInsertGrowableList(&cUnit.blockList, (intptr_t) entryBlock);
+ dvmInsertGrowableList(&cUnit.blockList, (intptr_t) exitBlock);
+
+ /* Current block to record parsed instructions */
+ BasicBlock *curBlock = dvmCompilerNewBB(kDalvikByteCode, numBlocks++);
+ curBlock->startOffset = 0;
+ dvmInsertGrowableList(&cUnit.blockList, (intptr_t) curBlock);
+ entryBlock->fallThrough = curBlock;
+ dvmCompilerSetBit(curBlock->predecessors, entryBlock->id);
+
+ /*
+ * Store back the number of blocks since new blocks may be created of
+ * accessing cUnit.
+ */
+ cUnit.numBlocks = numBlocks;
- /* Convert LIR into machine code. */
- dvmCompilerAssembleLIR(cUnit, info);
+ /* Identify code range in try blocks and set up the empty catch blocks */
+ processTryCatchBlocks(&cUnit);
- if (cUnit->assemblerStatus != kSuccess) {
- return false;
+ /* Parse all instructions and put them into containing basic blocks */
+ while (codePtr < codeEnd) {
+ MIR *insn = (MIR *) dvmCompilerNew(sizeof(MIR), true);
+ insn->offset = curOffset;
+ int width = parseInsn(codePtr, &insn->dalvikInsn, false);
+ insn->width = width;
+
+ /* Terminate when the data section is seen */
+ if (width == 0)
+ break;
+
+ dvmCompilerAppendMIR(curBlock, insn);
+
+ codePtr += width;
+ int flags = dexGetFlagsFromOpcode(insn->dalvikInsn.opcode);
+
+ if (flags & kInstrCanBranch) {
+ processCanBranch(&cUnit, curBlock, insn, curOffset, width, flags,
+ codePtr, codeEnd);
+ } else if (flags & kInstrCanReturn) {
+ curBlock->fallThrough = exitBlock;
+ dvmCompilerSetBit(exitBlock->predecessors, curBlock->id);
+ /*
+ * Terminate the current block if there are instructions
+ * afterwards.
+ */
+ if (codePtr < codeEnd) {
+ /*
+ * Create a fallthrough block for real instructions
+ * (incl. OP_NOP).
+ */
+ if (contentIsInsn(codePtr)) {
+ findBlock(&cUnit, curOffset + width,
+ /* split */
+ false,
+ /* create */
+ true);
+ }
+ }
+ } else if (flags & kInstrCanThrow) {
+ processCanThrow(&cUnit, curBlock, insn, curOffset, width, flags,
+ tryBlockAddr, codePtr, codeEnd);
+ } else if (flags & kInstrCanSwitch) {
+ processCanSwitch(&cUnit, curBlock, insn, curOffset, width, flags);
+ }
+ curOffset += width;
+ BasicBlock *nextBlock = findBlock(&cUnit, curOffset,
+ /* split */
+ false,
+ /* create */
+ false);
+ if (nextBlock) {
+ /*
+ * The next instruction could be the target of a previously parsed
+ * forward branch so a block is already created. If the current
+ * instruction is not an unconditional branch, connect them through
+ * the fall-through link.
+ */
+ assert(curBlock->fallThrough == NULL ||
+ curBlock->fallThrough == nextBlock ||
+ curBlock->fallThrough == exitBlock);
+
+ if ((curBlock->fallThrough == NULL) &&
+ !dexIsGoto(flags) &&
+ !(flags & kInstrCanReturn)) {
+ curBlock->fallThrough = nextBlock;
+ dvmCompilerSetBit(nextBlock->predecessors, curBlock->id);
+ }
+ curBlock = nextBlock;
+ }
}
- dvmCompilerDumpCompilationUnit(cUnit);
+ /* Adjust this value accordingly once inlining is performed */
+ cUnit.numDalvikRegisters = cUnit.method->registersSize;
+
+ /* Verify if all blocks are connected as claimed */
+ /* FIXME - to be disabled in the future */
+ dvmCompilerDataFlowAnalysisDispatcher(&cUnit, verifyPredInfo,
+ kAllNodes,
+ false /* isIterative */);
+
+
+ /* Perform SSA transformation for the whole method */
+ dvmCompilerMethodSSATransformation(&cUnit);
+ if (cUnit.printMe) dumpCFG(&cUnit, "/data/tombstones/");
+
+ /* Reset the compiler resource pool */
dvmCompilerArenaReset();
- return info->codeAddress != NULL;
+ return false;
}
{
BasicBlock *moveResultBB = invokeBB->fallThrough;
MIR *moveResultMIR = moveResultBB->firstMIRInsn;
- MIR *newGetterMIR = dvmCompilerNew(sizeof(MIR), true);
+ MIR *newGetterMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
DecodedInstruction getterInsn;
dexDecodeInstruction(calleeMethod->insns, &getterInsn);
dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newGetterMIR);
if (isPredicted) {
- MIR *invokeMIRSlow = dvmCompilerNew(sizeof(MIR), true);
+ MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
*invokeMIRSlow = *invokeMIR;
invokeMIR->dalvikInsn.opcode = kMirOpCheckInlinePrediction;
bool isPredicted,
bool isRange)
{
- MIR *newSetterMIR = dvmCompilerNew(sizeof(MIR), true);
+ MIR *newSetterMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
DecodedInstruction setterInsn;
dexDecodeInstruction(calleeMethod->insns, &setterInsn);
dvmCompilerInsertMIRAfter(invokeBB, invokeMIR, newSetterMIR);
if (isPredicted) {
- MIR *invokeMIRSlow = dvmCompilerNew(sizeof(MIR), true);
+ MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
*invokeMIRSlow = *invokeMIR;
invokeMIR->dalvikInsn.opcode = kMirOpCheckInlinePrediction;
MIR *invokeMIR,
BasicBlock *invokeBB)
{
- MIR *invokeMIRSlow = dvmCompilerNew(sizeof(MIR), true);
+ MIR *invokeMIRSlow = (MIR *)dvmCompilerNew(sizeof(MIR), true);
*invokeMIRSlow = *invokeMIR;
invokeMIR->dalvikInsn.opcode = kMirOpCheckInlinePrediction;
void dvmCompilerInlineMIR(CompilationUnit *cUnit)
{
- int i;
bool isRange = false;
+ GrowableListIterator iterator;
+ dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
/*
* Analyze the basic block containing an invoke to see if it can be inlined
*/
- for (i = 0; i < cUnit->numBlocks; i++) {
- BasicBlock *bb = cUnit->blockList[i];
+ while (true) {
+ BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+ if (bb == NULL) break;
if (bb->blockType != kDalvikByteCode)
continue;
MIR *lastMIRInsn = bb->lastMIRInsn;
#include "CompilerInternals.h"
/* Allocate a new basic block */
-BasicBlock *dvmCompilerNewBB(BBType blockType)
+BasicBlock *dvmCompilerNewBB(BBType blockType, int blockId)
{
- BasicBlock *bb = dvmCompilerNew(sizeof(BasicBlock), true);
+ BasicBlock *bb = (BasicBlock *)dvmCompilerNew(sizeof(BasicBlock), true);
bb->blockType = blockType;
+ bb->id = blockId;
+ bb->predecessors = dvmCompilerAllocBitVector(blockId > 32 ? blockId : 32,
+ true /* expandable */);
return bb;
}
*/
static void handlePhiPlacement(CompilationUnit *cUnit)
{
- BasicBlock *entry = cUnit->blockList[0];
- BasicBlock *loopBody = cUnit->blockList[1];
- BasicBlock *loopBranch = cUnit->blockList[2];
+ BasicBlock *entry =
+ (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 0);
+ BasicBlock *loopBody =
+ (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 1);
+ BasicBlock *loopBranch =
+ (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 2);
dvmCopyBitVector(entry->dataFlowInfo->defV,
loopBody->dataFlowInfo->liveInV);
BitVector *phiV = dvmCompilerAllocBitVector(cUnit->method->registersSize,
false);
+ BitVector *phi2V = dvmCompilerAllocBitVector(cUnit->method->registersSize,
+ false);
dvmIntersectBitVectors(phiV, entry->dataFlowInfo->defV,
loopBody->dataFlowInfo->defV);
- dvmIntersectBitVectors(phiV, entry->dataFlowInfo->defV,
+ dvmIntersectBitVectors(phi2V, entry->dataFlowInfo->defV,
loopBranch->dataFlowInfo->defV);
+ dvmUnifyBitVectors(phiV, phiV, phi2V);
/* Insert the PHI MIRs */
int i;
if (!dvmIsBitSet(phiV, i)) {
continue;
}
- MIR *phi = dvmCompilerNew(sizeof(MIR), true);
+ MIR *phi = (MIR *)dvmCompilerNew(sizeof(MIR), true);
phi->dalvikInsn.opcode = kMirOpPhi;
phi->dalvikInsn.vA = i;
dvmCompilerPrependMIR(loopBody, phi);
static void fillPhiNodeContents(CompilationUnit *cUnit)
{
- BasicBlock *entry = cUnit->blockList[0];
- BasicBlock *loopBody = cUnit->blockList[1];
- BasicBlock *loopBranch = cUnit->blockList[2];
+ BasicBlock *entry =
+ (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 0);
+ BasicBlock *loopBody =
+ (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 1);
+ BasicBlock *loopBranch =
+ (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 2);
MIR *mir;
for (mir = loopBody->firstMIRInsn; mir; mir = mir->next) {
int dalvikReg = mir->dalvikInsn.vA;
mir->ssaRep->numUses = 2;
- mir->ssaRep->uses = dvmCompilerNew(sizeof(int) * 2, false);
+ mir->ssaRep->uses = (int *)dvmCompilerNew(sizeof(int) * 2, false);
mir->ssaRep->uses[0] =
DECODE_REG(entry->dataFlowInfo->dalvikToSSAMap[dalvikReg]);
mir->ssaRep->uses[1] =
static bool isLoopOptimizable(CompilationUnit *cUnit)
{
unsigned int i;
- BasicBlock *loopBranch = cUnit->blockList[2];
+ BasicBlock *loopBranch =
+ (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 2);
LoopAnalysis *loopAnalysis = cUnit->loopAnalysis;
if (loopAnalysis->numBasicIV != 1) return false;
}
if (arrayAccessInfo == NULL) {
arrayAccessInfo =
- dvmCompilerNew(sizeof(ArrayAccessInfo), false);
+ (ArrayAccessInfo *)dvmCompilerNew(sizeof(ArrayAccessInfo),
+ false);
arrayAccessInfo->ivReg = ivInfo->basicSSAReg;
arrayAccessInfo->arrayReg = arrayReg;
arrayAccessInfo->maxC = (ivInfo->c > 0) ? ivInfo->c : 0;
arrayAccessInfo->minC = (ivInfo->c < 0) ? ivInfo->c : 0;
dvmInsertGrowableList(loopAnalysis->arrayAccessInfo,
- arrayAccessInfo);
+ (intptr_t) arrayAccessInfo);
}
break;
}
/* Returns true if the loop body cannot throw any exceptions */
static bool doLoopBodyCodeMotion(CompilationUnit *cUnit)
{
- BasicBlock *loopBody = cUnit->blockList[1];
+ BasicBlock *loopBody =
+ (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 1);
MIR *mir;
bool loopBodyCanThrow = false;
static void genHoistedChecks(CompilationUnit *cUnit)
{
unsigned int i;
- BasicBlock *entry = cUnit->blockList[0];
+ BasicBlock *entry =
+ (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 0);
LoopAnalysis *loopAnalysis = cUnit->loopAnalysis;
int globalMaxC = 0;
int globalMinC = 0;
idxReg = DECODE_REG(
dvmConvertSSARegToDalvik(cUnit, arrayAccessInfo->ivReg));
- MIR *rangeCheckMIR = dvmCompilerNew(sizeof(MIR), true);
+ MIR *rangeCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
rangeCheckMIR->dalvikInsn.opcode = (loopAnalysis->isCountUpLoop) ?
kMirOpNullNRangeUpCheck : kMirOpNullNRangeDownCheck;
rangeCheckMIR->dalvikInsn.vA = arrayReg;
if (loopAnalysis->arrayAccessInfo->numUsed != 0) {
if (loopAnalysis->isCountUpLoop) {
- MIR *boundCheckMIR = dvmCompilerNew(sizeof(MIR), true);
+ MIR *boundCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
boundCheckMIR->dalvikInsn.opcode = kMirOpLowerBound;
boundCheckMIR->dalvikInsn.vA = idxReg;
boundCheckMIR->dalvikInsn.vB = globalMinC;
} else {
if (loopAnalysis->loopBranchOpcode == OP_IF_LT ||
loopAnalysis->loopBranchOpcode == OP_IF_LE) {
- MIR *boundCheckMIR = dvmCompilerNew(sizeof(MIR), true);
+ MIR *boundCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
boundCheckMIR->dalvikInsn.opcode = kMirOpLowerBound;
boundCheckMIR->dalvikInsn.vA = loopAnalysis->endConditionReg;
boundCheckMIR->dalvikInsn.vB = globalMinC;
} else if (loopAnalysis->loopBranchOpcode == OP_IF_LTZ) {
/* Array index will fall below 0 */
if (globalMinC < 0) {
- MIR *boundCheckMIR = dvmCompilerNew(sizeof(MIR), true);
+ MIR *boundCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
boundCheckMIR->dalvikInsn.opcode = kMirOpPunt;
dvmCompilerAppendMIR(entry, boundCheckMIR);
}
} else if (loopAnalysis->loopBranchOpcode == OP_IF_LEZ) {
/* Array index will fall below 0 */
if (globalMinC < -1) {
- MIR *boundCheckMIR = dvmCompilerNew(sizeof(MIR), true);
+ MIR *boundCheckMIR = (MIR *)dvmCompilerNew(sizeof(MIR), true);
boundCheckMIR->dalvikInsn.opcode = kMirOpPunt;
dvmCompilerAppendMIR(entry, boundCheckMIR);
}
*/
bool dvmCompilerLoopOpt(CompilationUnit *cUnit)
{
- LoopAnalysis *loopAnalysis = dvmCompilerNew(sizeof(LoopAnalysis), true);
+ LoopAnalysis *loopAnalysis =
+ (LoopAnalysis *)dvmCompilerNew(sizeof(LoopAnalysis), true);
- assert(cUnit->blockList[0]->blockType == kTraceEntryBlock);
- assert(cUnit->blockList[2]->blockType == kDalvikByteCode);
- assert(cUnit->blockList[3]->blockType == kTraceExitBlock);
+ assert(((BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 0))
+ ->blockType == kTraceEntryBlock);
+ assert(((BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 2))
+ ->blockType == kDalvikByteCode);
+ assert(((BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList, 3))
+ ->blockType == kTraceExitBlock);
cUnit->loopAnalysis = loopAnalysis;
/*
* Find live-in variables to the loop body so that we can fake their
* definitions in the entry block.
*/
- dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerFindLiveIn);
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerFindLocalLiveIn,
+ kAllNodes,
+ false /* isIterative */);
/* Insert phi nodes to the loop body */
handlePhiPlacement(cUnit);
- dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion);
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion,
+ kAllNodes,
+ false /* isIterative */);
fillPhiNodeContents(cUnit);
/* Constant propagation */
cUnit->isConstantV = dvmAllocBitVector(cUnit->numSSARegs, false);
- cUnit->constantValues = dvmCompilerNew(sizeof(int) * cUnit->numSSARegs,
- true);
+ cUnit->constantValues =
+ (int *)dvmCompilerNew(sizeof(int) * cUnit->numSSARegs,
+ true);
dvmCompilerDataFlowAnalysisDispatcher(cUnit,
- dvmCompilerDoConstantPropagation);
+ dvmCompilerDoConstantPropagation,
+ kAllNodes,
+ false /* isIterative */);
DEBUG_LOOP(dumpConstants(cUnit);)
/* Find induction variables - basic and dependent */
- loopAnalysis->ivList = dvmCompilerNew(sizeof(GrowableList), true);
+ loopAnalysis->ivList =
+ (GrowableList *)dvmCompilerNew(sizeof(GrowableList), true);
dvmInitGrowableList(loopAnalysis->ivList, 4);
loopAnalysis->isIndVarV = dvmAllocBitVector(cUnit->numSSARegs, false);
dvmCompilerDataFlowAnalysisDispatcher(cUnit,
- dvmCompilerFindInductionVariables);
+ dvmCompilerFindInductionVariables,
+ kAllNodes,
+ false /* isIterative */);
DEBUG_LOOP(dumpIVList(cUnit);)
/* If the loop turns out to be non-optimizable, return early */
if (!isLoopOptimizable(cUnit))
return false;
- loopAnalysis->arrayAccessInfo = dvmCompilerNew(sizeof(GrowableList), true);
+ loopAnalysis->arrayAccessInfo =
+ (GrowableList *)dvmCompilerNew(sizeof(GrowableList), true);
dvmInitGrowableList(loopAnalysis->arrayAccessInfo, 4);
loopAnalysis->bodyIsClean = doLoopBodyCodeMotion(cUnit);
DEBUG_LOOP(dumpHoistedChecks(cUnit);)
--- /dev/null
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Dalvik.h"
+#include "Dataflow.h"
+#include "Loop.h"
+#include "libdex/DexOpcodes.h"
+
+/* Enter the node to the dfsOrder list then visit its successors */
+static void recordDFSPreOrder(CompilationUnit *cUnit, BasicBlock *block)
+{
+
+ if (block->visited) return;
+ block->visited = true;
+
+ /* Enqueue the block id */
+ dvmInsertGrowableList(&cUnit->dfsOrder, block->id);
+
+ if (block->taken) recordDFSPreOrder(cUnit, block->taken);
+ if (block->fallThrough) recordDFSPreOrder(cUnit, block->fallThrough);
+ if (block->successorBlockList.blockListType != kNotUsed) {
+ GrowableListIterator iterator;
+ dvmGrowableListIteratorInit(&block->successorBlockList.blocks,
+ &iterator);
+ while (true) {
+ SuccessorBlockInfo *successorBlockInfo =
+ (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+ if (successorBlockInfo == NULL) break;
+ BasicBlock *succBB = successorBlockInfo->block;
+ recordDFSPreOrder(cUnit, succBB);
+ }
+ }
+ return;
+}
+
+/* Sort the blocks by the Depth-First-Search pre-order */
+static void computeDFSOrder(CompilationUnit *cUnit)
+{
+ /* Initialize the DFS order list */
+ dvmInitGrowableList(&cUnit->dfsOrder, cUnit->numBlocks);
+
+
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerClearVisitedFlag,
+ kAllNodes,
+ false /* isIterative */);
+
+ recordDFSPreOrder(cUnit, cUnit->entryBlock);
+ cUnit->numReachableBlocks = cUnit->dfsOrder.numUsed;
+}
+
+/*
+ * Mark block bit on the per-Dalvik register vector to denote that Dalvik
+ * register idx is defined in BasicBlock bb.
+ */
+static bool fillDefBlockMatrix(CompilationUnit *cUnit, BasicBlock *bb)
+{
+ if (bb->dataFlowInfo == NULL) return false;
+
+ BitVectorIterator iterator;
+
+ dvmBitVectorIteratorInit(bb->dataFlowInfo->defV, &iterator);
+ while (true) {
+ int idx = dvmBitVectorIteratorNext(&iterator);
+ if (idx == -1) break;
+ /* Block bb defines register idx */
+ dvmCompilerSetBit(cUnit->defBlockMatrix[idx], bb->id);
+ }
+ return true;
+}
+
+static void computeDefBlockMatrix(CompilationUnit *cUnit)
+{
+ int numRegisters = cUnit->numDalvikRegisters;
+ /* Allocate numDalvikRegisters bit vector pointers */
+ cUnit->defBlockMatrix = (BitVector **)
+ dvmCompilerNew(sizeof(BitVector *) * numRegisters, true);
+ int i;
+
+ /* Initialize numRegister vectors with numBlocks bits each */
+ for (i = 0; i < numRegisters; i++) {
+ cUnit->defBlockMatrix[i] = dvmCompilerAllocBitVector(cUnit->numBlocks,
+ false);
+ }
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerFindLocalLiveIn,
+ kAllNodes,
+ false /* isIterative */);
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit, fillDefBlockMatrix,
+ kAllNodes,
+ false /* isIterative */);
+
+ /*
+ * Also set the incoming parameters as defs in the entry block.
+ * Only need to handle the parameters for the outer method.
+ */
+ int inReg = cUnit->method->registersSize - cUnit->method->insSize;
+ for (; inReg < cUnit->method->registersSize; inReg++) {
+ dvmCompilerSetBit(cUnit->defBlockMatrix[inReg],
+ cUnit->entryBlock->id);
+ }
+}
+
+/* Compute the post-order traversal of the CFG */
+static void computeDomPostOrderTraversal(CompilationUnit *cUnit, BasicBlock *bb)
+{
+ BitVectorIterator bvIterator;
+ dvmBitVectorIteratorInit(bb->iDominated, &bvIterator);
+ GrowableList *blockList = &cUnit->blockList;
+
+ /* Iterate through the dominated blocks first */
+ while (true) {
+ int bbIdx = dvmBitVectorIteratorNext(&bvIterator);
+ if (bbIdx == -1) break;
+ BasicBlock *dominatedBB =
+ (BasicBlock *) dvmGrowableListGetElement(blockList, bbIdx);
+ computeDomPostOrderTraversal(cUnit, dominatedBB);
+ }
+
+ /* Enter the current block id */
+ dvmInsertGrowableList(&cUnit->domPostOrderTraversal, bb->id);
+
+ /* hacky loop detection */
+ if (bb->taken && dvmIsBitSet(bb->dominators, bb->taken->id)) {
+ cUnit->hasLoop = true;
+ }
+}
+
+/* Worker function to compute the dominance frontier */
+static bool computeDominanceFrontier(CompilationUnit *cUnit, BasicBlock *bb)
+{
+ GrowableList *blockList = &cUnit->blockList;
+
+ /* Calculate DF_local */
+ if (bb->taken && !dvmIsBitSet(bb->taken->dominators, bb->id)) {
+ dvmSetBit(bb->domFrontier, bb->taken->id);
+ }
+ if (bb->fallThrough &&
+ !dvmIsBitSet(bb->fallThrough->dominators, bb->id)) {
+ dvmSetBit(bb->domFrontier, bb->fallThrough->id);
+ }
+ if (bb->successorBlockList.blockListType != kNotUsed) {
+ GrowableListIterator iterator;
+ dvmGrowableListIteratorInit(&bb->successorBlockList.blocks,
+ &iterator);
+ while (true) {
+ SuccessorBlockInfo *successorBlockInfo =
+ (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+ if (successorBlockInfo == NULL) break;
+ BasicBlock *succBB = successorBlockInfo->block;
+ if (!dvmIsBitSet(succBB->dominators, bb->id)) {
+ dvmSetBit(bb->domFrontier, succBB->id);
+ }
+ }
+ }
+
+ /* Calculate DF_up */
+ BitVectorIterator bvIterator;
+ dvmBitVectorIteratorInit(bb->iDominated, &bvIterator);
+ while (true) {
+ int dominatedIdx = dvmBitVectorIteratorNext(&bvIterator);
+ if (dominatedIdx == -1) break;
+ BasicBlock *dominatedBB = (BasicBlock *)
+ dvmGrowableListGetElement(blockList, dominatedIdx);
+ BitVectorIterator dfIterator;
+ dvmBitVectorIteratorInit(dominatedBB->domFrontier, &dfIterator);
+ while (true) {
+ int dfUpIdx = dvmBitVectorIteratorNext(&dfIterator);
+ if (dfUpIdx == -1) break;
+ BasicBlock *dfUpBlock = (BasicBlock *)
+ dvmGrowableListGetElement(blockList, dfUpIdx);
+ if (!dvmIsBitSet(dfUpBlock->dominators, bb->id)) {
+ dvmSetBit(bb->domFrontier, dfUpBlock->id);
+ }
+ }
+ }
+ if (cUnit->printMe) {
+ char blockName[BLOCK_NAME_LEN];
+ dvmGetBlockName(bb, blockName);
+ dvmDumpBlockBitVector(blockList, blockName, bb->domFrontier,
+ cUnit->numBlocks);
+ }
+
+ return true;
+}
+
+/* Worker function for initializing domination-related data structures */
+static bool initializeDominationInfo(CompilationUnit *cUnit, BasicBlock *bb)
+{
+ int numTotalBlocks = cUnit->blockList.numUsed;
+
+ bb->dominators = dvmCompilerAllocBitVector(numTotalBlocks,
+ false /* expandable */);
+ bb->iDominated = dvmCompilerAllocBitVector(numTotalBlocks,
+ false /* expandable */);
+ bb->domFrontier = dvmCompilerAllocBitVector(numTotalBlocks,
+ false /* expandable */);
+ /* Set all bits in the dominator vector */
+ dvmSetInitialBits(bb->dominators, numTotalBlocks);
+
+ return true;
+}
+
+/* Worker function to compute each block's dominators */
+static bool computeBlockDominators(CompilationUnit *cUnit, BasicBlock *bb)
+{
+ GrowableList *blockList = &cUnit->blockList;
+ int numTotalBlocks = blockList->numUsed;
+ BitVector *tempBlockV = cUnit->tempBlockV;
+ BitVectorIterator bvIterator;
+
+ /*
+ * The dominator of the entry block has been preset to itself and we need
+ * to skip the calculation here.
+ */
+ if (bb == cUnit->entryBlock) return false;
+
+ dvmSetInitialBits(tempBlockV, numTotalBlocks);
+
+ /* Iterate through the predecessors */
+ dvmBitVectorIteratorInit(bb->predecessors, &bvIterator);
+ while (true) {
+ int predIdx = dvmBitVectorIteratorNext(&bvIterator);
+ if (predIdx == -1) break;
+ BasicBlock *predBB = (BasicBlock *) dvmGrowableListGetElement(
+ blockList, predIdx);
+ /* tempBlockV = tempBlockV ^ dominators */
+ dvmIntersectBitVectors(tempBlockV, tempBlockV, predBB->dominators);
+ }
+ dvmSetBit(tempBlockV, bb->id);
+ if (dvmCompareBitVectors(tempBlockV, bb->dominators)) {
+ dvmCopyBitVector(bb->dominators, tempBlockV);
+ return true;
+ }
+ return false;
+}
+
+/* Worker function to compute the idom */
+static bool computeImmediateDominator(CompilationUnit *cUnit, BasicBlock *bb)
+{
+ GrowableList *blockList = &cUnit->blockList;
+ BitVector *tempBlockV = cUnit->tempBlockV;
+ BitVectorIterator bvIterator;
+ BasicBlock *iDom;
+
+ if (bb == cUnit->entryBlock) return false;
+
+ dvmCopyBitVector(tempBlockV, bb->dominators);
+ dvmClearBit(tempBlockV, bb->id);
+ dvmBitVectorIteratorInit(tempBlockV, &bvIterator);
+
+ /* Should not see any dead block */
+ assert(dvmCountSetBits(tempBlockV) != 0);
+ if (dvmCountSetBits(tempBlockV) == 1) {
+ iDom = (BasicBlock *) dvmGrowableListGetElement(
+ blockList, dvmBitVectorIteratorNext(&bvIterator));
+ bb->iDom = iDom;
+ } else {
+ int iDomIdx = dvmBitVectorIteratorNext(&bvIterator);
+ assert(iDomIdx != -1);
+ while (true) {
+ int nextDom = dvmBitVectorIteratorNext(&bvIterator);
+ if (nextDom == -1) break;
+ BasicBlock *nextDomBB = (BasicBlock *)
+ dvmGrowableListGetElement(blockList, nextDom);
+ /* iDom dominates nextDom - set new iDom */
+ if (dvmIsBitSet(nextDomBB->dominators, iDomIdx)) {
+ iDomIdx = nextDom;
+ }
+
+ }
+ iDom = (BasicBlock *) dvmGrowableListGetElement(blockList, iDomIdx);
+ /* Set the immediate dominator block for bb */
+ bb->iDom = iDom;
+ }
+ /* Add bb to the iDominated set of the immediate dominator block */
+ dvmCompilerSetBit(iDom->iDominated, bb->id);
+ return true;
+}
+
+/* Compute dominators, immediate dominator, and dominance fronter */
+static void computeDominators(CompilationUnit *cUnit)
+{
+ int numReachableBlocks = cUnit->numReachableBlocks;
+ int numTotalBlocks = cUnit->blockList.numUsed;
+
+ /* Initialize domination-related data structures */
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit, initializeDominationInfo,
+ kReachableNodes,
+ false /* isIterative */);
+
+ /* Set the dominator for the root node */
+ dvmClearAllBits(cUnit->entryBlock->dominators);
+ dvmSetBit(cUnit->entryBlock->dominators, cUnit->entryBlock->id);
+
+ cUnit->tempBlockV = dvmCompilerAllocBitVector(numTotalBlocks,
+ false /* expandable */);
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit, computeBlockDominators,
+ kPreOrderDFSTraversal,
+ true /* isIterative */);
+
+ cUnit->entryBlock->iDom = NULL;
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit, computeImmediateDominator,
+ kReachableNodes,
+ false /* isIterative */);
+
+ /*
+ * Now go ahead and compute the post order traversal based on the
+ * iDominated sets.
+ */
+ dvmInitGrowableList(&cUnit->domPostOrderTraversal, numReachableBlocks);
+ computeDomPostOrderTraversal(cUnit, cUnit->entryBlock);
+ assert(cUnit->domPostOrderTraversal.numUsed ==
+ (unsigned) cUnit->numReachableBlocks);
+
+ /* Now compute the dominance frontier for each block */
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit, computeDominanceFrontier,
+ kPostOrderDOMTraversal,
+ false /* isIterative */);
+}
+
+/*
+ * Perform dest U= src1 ^ ~src2
+ * This is probably not general enough to be placed in BitVector.[ch].
+ */
+static void computeSuccLiveIn(BitVector *dest,
+ const BitVector *src1,
+ const BitVector *src2)
+{
+ if (dest->storageSize != src1->storageSize ||
+ dest->storageSize != src2->storageSize ||
+ dest->expandable != src1->expandable ||
+ dest->expandable != src2->expandable) {
+ LOGE("Incompatible set properties");
+ dvmAbort();
+ }
+
+ unsigned int idx;
+ for (idx = 0; idx < dest->storageSize; idx++) {
+ dest->storage[idx] |= src1->storage[idx] & ~src2->storage[idx];
+ }
+}
+
+/*
+ * Iterate through all successor blocks and propagate up the live-in sets.
+ * The calculated result is used for phi-node pruning - where we only need to
+ * insert a phi node if the variable is live-in to the block.
+ */
+static bool computeBlockLiveIns(CompilationUnit *cUnit, BasicBlock *bb)
+{
+ BitVector *tempDalvikRegisterV = cUnit->tempDalvikRegisterV;
+
+ if (bb->dataFlowInfo == NULL) return false;
+ dvmCopyBitVector(tempDalvikRegisterV, bb->dataFlowInfo->liveInV);
+ if (bb->taken && bb->taken->dataFlowInfo)
+ computeSuccLiveIn(tempDalvikRegisterV, bb->taken->dataFlowInfo->liveInV,
+ bb->dataFlowInfo->defV);
+ if (bb->fallThrough && bb->fallThrough->dataFlowInfo)
+ computeSuccLiveIn(tempDalvikRegisterV,
+ bb->fallThrough->dataFlowInfo->liveInV,
+ bb->dataFlowInfo->defV);
+ if (bb->successorBlockList.blockListType != kNotUsed) {
+ GrowableListIterator iterator;
+ dvmGrowableListIteratorInit(&bb->successorBlockList.blocks,
+ &iterator);
+ while (true) {
+ SuccessorBlockInfo *successorBlockInfo =
+ (SuccessorBlockInfo *) dvmGrowableListIteratorNext(&iterator);
+ if (successorBlockInfo == NULL) break;
+ BasicBlock *succBB = successorBlockInfo->block;
+ if (succBB->dataFlowInfo) {
+ computeSuccLiveIn(tempDalvikRegisterV,
+ succBB->dataFlowInfo->liveInV,
+ bb->dataFlowInfo->defV);
+ }
+ }
+ }
+ if (dvmCompareBitVectors(tempDalvikRegisterV, bb->dataFlowInfo->liveInV)) {
+ dvmCopyBitVector(bb->dataFlowInfo->liveInV, tempDalvikRegisterV);
+ return true;
+ }
+ return false;
+}
+
+/* Insert phi nodes to for each variable to the dominance frontiers */
+static void insertPhiNodes(CompilationUnit *cUnit)
+{
+ int dalvikReg;
+ const GrowableList *blockList = &cUnit->blockList;
+ BitVector *phiBlocks =
+ dvmCompilerAllocBitVector(cUnit->numDalvikRegisters, false);
+ BitVector *tmpBlocks =
+ dvmCompilerAllocBitVector(cUnit->numDalvikRegisters, false);
+ BitVector *inputBlocks =
+ dvmCompilerAllocBitVector(cUnit->numDalvikRegisters, false);
+
+ cUnit->tempDalvikRegisterV =
+ dvmCompilerAllocBitVector(cUnit->numDalvikRegisters, false);
+
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit, computeBlockLiveIns,
+ kPostOrderDFSTraversal,
+ true /* isIterative */);
+
+ /* Iterate through each Dalvik register */
+ for (dalvikReg = 0; dalvikReg < cUnit->numDalvikRegisters; dalvikReg++) {
+ bool change;
+ BitVectorIterator iterator;
+
+ dvmCopyBitVector(inputBlocks, cUnit->defBlockMatrix[dalvikReg]);
+ dvmClearAllBits(phiBlocks);
+ /* Calculate the phi blocks for each Dalvik register */
+ do {
+ change = false;
+ dvmClearAllBits(tmpBlocks);
+ dvmBitVectorIteratorInit(inputBlocks, &iterator);
+ while (true) {
+ int idx = dvmBitVectorIteratorNext(&iterator);
+ if (idx == -1) break;
+ BasicBlock *defBB =
+ (BasicBlock *) dvmGrowableListGetElement(blockList, idx);
+ /* Merge the dominance frontier to tmpBlocks */
+ dvmUnifyBitVectors(tmpBlocks, tmpBlocks, defBB->domFrontier);
+ }
+ if (dvmCompareBitVectors(phiBlocks, tmpBlocks)) {
+ change = true;
+ dvmCopyBitVector(phiBlocks, tmpBlocks);
+
+ /*
+ * Iterate through the original blocks plus the new ones in
+ * the dominance frontier.
+ */
+ dvmCopyBitVector(inputBlocks, phiBlocks);
+ dvmUnifyBitVectors(inputBlocks, inputBlocks,
+ cUnit->defBlockMatrix[dalvikReg]);
+ }
+ } while (change);
+
+ /*
+ * Insert a phi node for dalvikReg in the phiBlocks if the Dalvik
+ * register is in the live-in set.
+ */
+ dvmBitVectorIteratorInit(phiBlocks, &iterator);
+ while (true) {
+ int idx = dvmBitVectorIteratorNext(&iterator);
+ if (idx == -1) break;
+ BasicBlock *phiBB =
+ (BasicBlock *) dvmGrowableListGetElement(blockList, idx);
+ /* Variable will be clobbered before being used - no need for phi */
+ if (!dvmIsBitSet(phiBB->dataFlowInfo->liveInV, dalvikReg)) continue;
+ MIR *phi = (MIR *) dvmCompilerNew(sizeof(MIR), true);
+ phi->dalvikInsn.opcode = kMirOpPhi;
+ phi->dalvikInsn.vA = dalvikReg;
+ phi->offset = phiBB->startOffset;
+ dvmCompilerPrependMIR(phiBB, phi);
+ }
+ }
+}
+
+/*
+ * Worker function to insert phi-operands with latest SSA names from
+ * predecessor blocks
+ */
+static bool insertPhiNodeOperands(CompilationUnit *cUnit, BasicBlock *bb)
+{
+ BitVector *ssaRegV = cUnit->tempSSARegisterV;
+ BitVectorIterator bvIterator;
+ GrowableList *blockList = &cUnit->blockList;
+ MIR *mir;
+
+ /* Phi nodes are at the beginning of each block */
+ for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
+ if (mir->dalvikInsn.opcode != kMirOpPhi) return true;
+ int ssaReg = mir->ssaRep->defs[0];
+ int encodedDalvikValue =
+ (int) dvmGrowableListGetElement(cUnit->ssaToDalvikMap, ssaReg);
+ int dalvikReg = DECODE_REG(encodedDalvikValue);
+
+ dvmClearAllBits(ssaRegV);
+
+ /* Iterate through the predecessors */
+ dvmBitVectorIteratorInit(bb->predecessors, &bvIterator);
+ while (true) {
+ int predIdx = dvmBitVectorIteratorNext(&bvIterator);
+ if (predIdx == -1) break;
+ BasicBlock *predBB = (BasicBlock *) dvmGrowableListGetElement(
+ blockList, predIdx);
+ int encodedSSAValue =
+ predBB->dataFlowInfo->dalvikToSSAMap[dalvikReg];
+ int ssaReg = DECODE_REG(encodedSSAValue);
+ dvmSetBit(ssaRegV, ssaReg);
+ }
+
+ /* Count the number of SSA registers for a Dalvik register */
+ int numUses = dvmCountSetBits(ssaRegV);
+ mir->ssaRep->numUses = numUses;
+ mir->ssaRep->uses =
+ (int *) dvmCompilerNew(sizeof(int) * numUses, false);
+ mir->ssaRep->fpUse =
+ (bool *) dvmCompilerNew(sizeof(bool) * numUses, false);
+
+ BitVectorIterator phiIterator;
+
+ dvmBitVectorIteratorInit(ssaRegV, &phiIterator);
+ int *usePtr = mir->ssaRep->uses;
+
+ /* Set the uses array for the phi node */
+ while (true) {
+ int ssaRegIdx = dvmBitVectorIteratorNext(&phiIterator);
+ if (ssaRegIdx == -1) break;
+ *usePtr++ = ssaRegIdx;
+ }
+ }
+
+ return true;
+}
+
+/* Perform SSA transformation for the whole method */
+void dvmCompilerMethodSSATransformation(CompilationUnit *cUnit)
+{
+ /* Compute the DFS order */
+ computeDFSOrder(cUnit);
+
+ /* Compute the dominator info */
+ computeDominators(cUnit);
+
+ /* Allocate data structures in preparation for SSA conversion */
+ dvmInitializeSSAConversion(cUnit);
+
+ /* Find out the "Dalvik reg def x block" relation */
+ computeDefBlockMatrix(cUnit);
+
+ /* Insert phi nodes to dominance frontiers for all variables */
+ insertPhiNodes(cUnit);
+
+ /* Rename register names by local defs and phi nodes */
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit, dvmCompilerDoSSAConversion,
+ kPreOrderDFSTraversal,
+ false /* isIterative */);
+
+ /*
+ * Shared temp bit vector used by each block to count the number of defs
+ * from all the predecessor blocks.
+ */
+ cUnit->tempSSARegisterV = dvmCompilerAllocBitVector(cUnit->numSSARegs,
+ false);
+
+ /* Insert phi-operands with latest SSA names from predecessor blocks */
+ dvmCompilerDataFlowAnalysisDispatcher(cUnit, insertPhiNodeOperands,
+ kReachableNodes,
+ false /* isIterative */);
+}
#include "CompilerInternals.h"
#include "Dataflow.h"
-typedef struct LiveRange {
- int ssaName;
- bool active;
- int first;
- int last;
-} LiveRange;
-
-static int computeLiveRange(LiveRange *list, BasicBlock *bb, int seqNum)
-{
- MIR *mir;
- int i;
-
- if (bb->blockType != kDalvikByteCode &&
- bb->blockType != kTraceEntryBlock)
- return seqNum;
-
- for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
- SSARepresentation *ssaRep = mir->ssaRep;
- mir->seqNum = seqNum;
- if (ssaRep) {
- for (i=0; i< ssaRep->numUses; i++) {
- int reg = ssaRep->uses[i];
- list[reg].first = MIN(list[reg].first, seqNum);
- list[reg].active = true;
- }
- for (i=0; i< ssaRep->numDefs; i++) {
- int reg = ssaRep->defs[i];
- list[reg].last = MAX(list[reg].last, seqNum + 1);
- list[reg].active = true;
- }
- seqNum += 2;
- }
- }
- return seqNum;
-}
-
/*
* Quick & dirty - make FP usage sticky. This is strictly a hint - local
* code generation will handle misses. It might be worthwhile to collaborate
}
}
-/*
- * Determine whether to use simple or aggressive register allocation. In
- * general, loops and full methods will get aggressive.
- */
-static bool simpleTrace(CompilationUnit *cUnit)
-{
- //TODO: flesh out
- return true;
-}
+static const RegLocation freshLoc = {kLocDalvikFrame, 0, 0, INVALID_REG,
+ INVALID_REG, INVALID_SREG};
/*
- * Target-independent register allocation. Requires target-dependent
- * helper functions and assumes free list, temp list and spill region.
- * Uses a variant of linear scan and produces a mapping between SSA names
- * and location. Location may be original Dalvik register, hardware
- * register or spill location.
- *
- * Method:
- * 0. Allocate the structure to hold the SSA name life ranges
- * 1. Number each MIR instruction, counting by 2.
- * +0 -> The "read" of the operands
- * +1 -> The definition of the target resource
- * 2. Compute live ranges for all SSA names *not* including the
- * subscript 0 original Dalvik names. Phi functions ignored
- * at this point.
- * 3. Sort the live range list by lowest range start.
- * 4. Process and remove all Phi functions.
- * o If there is no live range collisions among all operands and
- * the target of a Phi function, collapse operands and target
- * and rewrite using target SSA name.
- * o If there is a collision, introduce copies.
- * 5. Allocate in order of increasing live range start.
+ * Local register allocation for simple traces. Most of the work for
+ * local allocation is done on the fly. Here we do some initialization
+ * and type inference.
*/
-static const RegLocation freshLoc = {kLocDalvikFrame, 0, 0, INVALID_REG,
- INVALID_REG, INVALID_SREG};
-void dvmCompilerRegAlloc(CompilationUnit *cUnit)
+void dvmCompilerLocalRegAlloc(CompilationUnit *cUnit)
{
int i;
- int seqNum = 0;
- LiveRange *ranges;
RegLocation *loc;
/* Allocate the location map */
}
cUnit->regLocation = loc;
+ GrowableListIterator iterator;
+
+ dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
/* Do type inference pass */
- for (i=0; i < cUnit->numBlocks; i++) {
- inferTypes(cUnit, cUnit->blockList[i]);
+ while (true) {
+ BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+ if (bb == NULL) break;
+ inferTypes(cUnit, bb);
}
- if (simpleTrace(cUnit)) {
- /*
- * Just rename everything back to subscript 0 names and don't do
- * any explicit promotion. Local allocator will opportunistically
- * promote on the fly.
- */
- for (i=0; i < cUnit->numSSARegs; i++) {
- cUnit->regLocation[i].sRegLow =
+ /* Remap SSA names back to original frame locations. */
+ for (i=0; i < cUnit->numSSARegs; i++) {
+ cUnit->regLocation[i].sRegLow =
DECODE_REG(dvmConvertSSARegToDalvik(cUnit, loc[i].sRegLow));
- }
- } else {
- // Compute live ranges
- ranges = dvmCompilerNew(cUnit->numSSARegs * sizeof(*ranges), true);
- for (i=0; i < cUnit->numSSARegs; i++)
- ranges[i].active = false;
- seqNum = computeLiveRange(ranges, cUnit->blockList[i], seqNum);
- //TODO: phi squash & linear scan promotion
}
}
LOGI("Total arena pages for JIT: %d", numArenaBlocks);
goto retry;
}
- return NULL;
+ /* Should not reach here */
+ dvmAbort();
}
/* Reclaim all the arena blocks allocated so far */
{
gList->numAllocated = initLength;
gList->numUsed = 0;
- gList->elemList = (void **) dvmCompilerNew(sizeof(void *) * initLength,
- true);
+ gList->elemList = (intptr_t *) dvmCompilerNew(sizeof(intptr_t) * initLength,
+ true);
}
/* Expand the capacity of a growable list */
} else {
newLength += 128;
}
- void *newArray = dvmCompilerNew(sizeof(void *) * newLength, true);
- memcpy(newArray, gList->elemList, sizeof(void *) * gList->numAllocated);
+ intptr_t *newArray =
+ (intptr_t *) dvmCompilerNew(sizeof(intptr_t) * newLength, true);
+ memcpy(newArray, gList->elemList, sizeof(intptr_t) * gList->numAllocated);
gList->numAllocated = newLength;
gList->elemList = newArray;
}
/* Insert a new element into the growable list */
-void dvmInsertGrowableList(GrowableList *gList, void *elem)
+void dvmInsertGrowableList(GrowableList *gList, intptr_t elem)
{
assert(gList->numAllocated != 0);
if (gList->numUsed == gList->numAllocated) {
gList->elemList[gList->numUsed++] = elem;
}
+void dvmGrowableListIteratorInit(GrowableList *gList,
+ GrowableListIterator *iterator)
+{
+ iterator->list = gList;
+ iterator->idx = 0;
+ iterator->size = gList->numUsed;
+}
+
+intptr_t dvmGrowableListIteratorNext(GrowableListIterator *iterator)
+{
+ assert(iterator->size == iterator->list->numUsed);
+ if (iterator->idx == iterator->size) return 0;
+ return iterator->list->elemList[iterator->idx++];
+}
+
+intptr_t dvmGrowableListGetElement(const GrowableList *gList, size_t idx)
+{
+ assert(idx < gList->numUsed);
+ return gList->elemList[idx];
+}
+
/* Debug Utility - dump a compilation unit */
void dvmCompilerDumpCompilationUnit(CompilationUnit *cUnit)
{
- int i;
BasicBlock *bb;
char *blockTypeNames[] = {
"Normal Chaining Cell",
cUnit->method->name);
LOGD("%d insns", dvmGetMethodInsnsSize(cUnit->method));
LOGD("%d blocks in total", cUnit->numBlocks);
+ GrowableListIterator iterator;
+
+ dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
- for (i = 0; i < cUnit->numBlocks; i++) {
- bb = cUnit->blockList[i];
+ while (true) {
+ bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+ if (bb == NULL) break;
LOGD("Block %d (%s) (insn %04x - %04x%s)\n",
bb->id,
blockTypeNames[bb->blockType],
* NOTE: this is the sister implementation of dvmAllocBitVector. In this version
* memory is allocated from the compiler arena.
*/
-BitVector* dvmCompilerAllocBitVector(int startBits, bool expandable)
+BitVector* dvmCompilerAllocBitVector(unsigned int startBits, bool expandable)
{
BitVector* bv;
- int count;
+ unsigned int count;
assert(sizeof(bv->storage[0]) == 4); /* assuming 32-bit units */
- assert(startBits >= 0);
bv = (BitVector*) dvmCompilerNew(sizeof(BitVector), false);
* NOTE: this is the sister implementation of dvmSetBit. In this version
* memory is allocated from the compiler arena.
*/
-bool dvmCompilerSetBit(BitVector *pBits, int num)
+bool dvmCompilerSetBit(BitVector *pBits, unsigned int num)
{
- assert(num >= 0);
- if (num >= pBits->storageSize * (int)sizeof(u4) * 8) {
+ if (num >= pBits->storageSize * sizeof(u4) * 8) {
if (!pBits->expandable)
- return false;
+ dvmAbort();
/* Round up to word boundaries for "num+1" bits */
- int newSize = (num + 1 + 31) >> 5;
+ unsigned int newSize = (num + 1 + 31) >> 5;
assert(newSize > pBits->storageSize);
- u4 *newStorage = dvmCompilerNew(newSize * sizeof(u4), false);
+ u4 *newStorage = (u4*)dvmCompilerNew(newSize * sizeof(u4), false);
memcpy(newStorage, pBits->storage, pBits->storageSize * sizeof(u4));
memset(&newStorage[pBits->storageSize], 0,
(newSize - pBits->storageSize) * sizeof(u4));
return true;
}
+/*
+ * Mark the specified bit as "unset".
+ *
+ * Returns "false" if the bit is outside the range of the vector and we're
+ * not allowed to expand.
+ *
+ * NOTE: this is the sister implementation of dvmClearBit. In this version
+ * memory is allocated from the compiler arena.
+ */
+bool dvmCompilerClearBit(BitVector *pBits, unsigned int num)
+{
+ if (num >= pBits->storageSize * sizeof(u4) * 8) {
+ LOGE("Trying to clear a bit that is not set in the vector yet!");
+ dvmAbort();
+ }
+
+ pBits->storage[num >> 5] &= ~(1 << (num & 0x1f));
+ return true;
+}
+
+/*
+ * If set is true, mark all bits as 1. Otherwise mark all bits as 0.
+ */
+void dvmCompilerMarkAllBits(BitVector *pBits, bool set)
+{
+ int value = set ? -1 : 0;
+ memset(pBits->storage, value, pBits->storageSize * (int)sizeof(u4));
+}
+
void dvmDebugBitVector(char *msg, const BitVector *bv, int length)
{
int i;
*/
longjmp(*cUnit->bailPtr, 1);
}
+
+void dvmDumpBlockBitVector(const GrowableList *blocks, char *msg,
+ const BitVector *bv, int length)
+{
+ int i;
+
+ LOGE("%s", msg);
+ for (i = 0; i < length; i++) {
+ if (dvmIsBitSet(bv, i)) {
+ BasicBlock *bb =
+ (BasicBlock *) dvmGrowableListGetElement(blocks, i);
+ char blockName[BLOCK_NAME_LEN];
+ dvmGetBlockName(bb, blockName);
+ LOGE("Bit %d / %s is set", i, blockName);
+ }
+ }
+}
+
+void dvmGetBlockName(BasicBlock *bb, char *name)
+{
+ switch (bb->blockType) {
+ case kMethodEntryBlock:
+ snprintf(name, BLOCK_NAME_LEN, "entry");
+ break;
+ case kMethodExitBlock:
+ snprintf(name, BLOCK_NAME_LEN, "exit");
+ break;
+ case kDalvikByteCode:
+ snprintf(name, BLOCK_NAME_LEN, "block%04x", bb->startOffset);
+ break;
+ case kExceptionHandling:
+ snprintf(name, BLOCK_NAME_LEN, "exception%04x", bb->startOffset);
+ break;
+ default:
+ snprintf(name, BLOCK_NAME_LEN, "??");
+ break;
+ }
+}
void dvmCompilerPatchInlineCache(void);
/* Implemented in codegen/<target>/Ralloc.c */
-void dvmCompilerRegAlloc(CompilationUnit *cUnit);
+void dvmCompilerLocalRegAlloc(CompilationUnit *cUnit);
/* Implemented in codegen/<target>/Thumb<version>Util.c */
void dvmCompilerInitializeRegAlloc(CompilationUnit *cUnit);
dvmCompilerAbort(cUnit);
}
-/*
- * FIXME - this needs to also check the preserved pool once we start
- * start using preserved registers.
- */
extern RegisterInfo *dvmCompilerIsLive(CompilationUnit *cUnit, int reg)
{
RegisterInfo *p = cUnit->regPool->coreTemps;
LOGD("installed code is at %p\n", cUnit->baseAddr);
LOGD("total size is %d bytes\n", cUnit->totalSize);
for (lirInsn = cUnit->firstLIRInsn; lirInsn; lirInsn = lirInsn->next) {
- dvmDumpLIRInsn(lirInsn, cUnit->baseAddr);
+ dvmDumpLIRInsn(lirInsn, (unsigned char *) cUnit->baseAddr);
}
for (lirInsn = cUnit->wordList; lirInsn; lirInsn = lirInsn->next) {
armLIR = (ArmLIR *) lirInsn;
armLIR->operands[0]);
}
}
+
+/* Target-specific cache flushing */
+int dvmCompilerCacheFlush(long start, long end, long flags)
+{
+ return cacheflush(start, end, flags);
+}
int numFPTemps;
RegisterInfo *FPTemps;
int nextFPTemp;
- int numCoreRegs;
- RegisterInfo *coreRegs;
- int numFPRegs;
- RegisterInfo *FPRegs;
} RegisterPool;
typedef enum ResourceEncodingPos {
kThumb2Bfc, /* bfc [11110011011011110] [0] imm3[14-12]
rd[11-8] imm2[7-6] [0] msb[4-0] */
kThumb2Dmb, /* dmb [1111001110111111100011110101] option[3-0] */
+ kThumb2LdrPcReln12, /* ldr rd,[pc,-#imm12] [1111100011011111] rt[15-12]
+ imm12[11-0] */
kArmLast,
} ArmOpcode;
#include "../../CompilerInternals.h"
#include "ArmLIR.h"
#include "Codegen.h"
-#include <unistd.h> /* for cacheflush */
#include <sys/mman.h> /* for protection change */
#define MAX_ASSEMBLER_RETRIES 10
kFmtBitBlt, 3, 0, kFmtUnused, -1, -1, kFmtUnused, -1, -1,
kFmtUnused, -1, -1, IS_UNARY_OP,
"dmb","#!0B",2),
+ ENCODING_MAP(kThumb2LdrPcReln12, 0xf85f0000,
+ kFmtBitBlt, 15, 12, kFmtBitBlt, 11, 0, kFmtUnused, -1, -1,
+ kFmtUnused, -1, -1,
+ IS_BINARY_OP | REG_DEF0 | REG_USE_PC | IS_LOAD,
+ "ldr", "r!0d, [rpc, -#!1d]", 2),
};
/*
int delta = target - pc;
if (delta > 126 || delta < 0) {
/* Convert to cmp rx,#0 / b[eq/ne] tgt pair */
- ArmLIR *newInst = dvmCompilerNew(sizeof(ArmLIR), true);
+ ArmLIR *newInst =
+ (ArmLIR *)dvmCompilerNew(sizeof(ArmLIR), true);
/* Make new branch instruction and insert after */
newInst->opcode = kThumbBCond;
newInst->operands[0] = 0;
/*
* Translation layout in the code cache. Note that the codeAddress pointer
* in JitTable will point directly to the code body (field codeAddress). The
- * chain cell offset codeAddress - 2, and (if present) executionCount is at
- * codeAddress - 6.
+ * chain cell offset codeAddress - 2, and the address of the trace profile
+ * counter is at codeAddress - 6.
*
* +----------------------------+
- * | Execution count | -> [Optional] 4 bytes
+ * | Trace Profile Counter addr | -> 4 bytes
* +----------------------------+
* +--| Offset to chain cell counts| -> 2 bytes
* | +----------------------------+
- * | | Code body | -> Start address for translation
- * | | | variable in 2-byte chunks
- * | . . (JitTable's codeAddress points here)
+ * | | Trace profile code | <- entry point when profiling
+ * | . - - - - - - - .
+ * | | Code body | <- entry point when not profiling
* | . .
* | | |
* | +----------------------------+
- * | | Chaining Cells | -> 12/16 bytes each, must be 4 byte aligned
+ * | | Chaining Cells | -> 12/16 bytes, 4 byte aligned
* | . .
* | . .
* | | |
chainCellOffsetLIR->operands[0] == CHAIN_CELL_OFFSET_TAG);
/*
- * Replace the CHAIN_CELL_OFFSET_TAG with the real value. If trace
- * profiling is enabled, subtract 4 (occupied by the counter word) from
- * the absolute offset as the value stored in chainCellOffsetLIR is the
- * delta from &chainCellOffsetLIR to &ChainCellCounts.
+ * Adjust the CHAIN_CELL_OFFSET_TAG LIR's offset to remove the
+ * space occupied by the pointer to the trace profiling counter.
*/
- chainCellOffsetLIR->operands[0] =
- gDvmJit.profile ? (chainCellOffset - 4) : chainCellOffset;
+ chainCellOffsetLIR->operands[0] = chainCellOffset - 4;
offset += sizeof(chainCellCounts) + descSize;
}
/* Allocate enough space for the code block */
- cUnit->codeBuffer = dvmCompilerNew(chainCellOffset, true);
+ cUnit->codeBuffer = (unsigned char *)dvmCompilerNew(chainCellOffset, true);
if (cUnit->codeBuffer == NULL) {
LOGE("Code buffer allocation failure\n");
cUnit->baseAddr = NULL;
installDataContent(cUnit);
/* Flush dcache and invalidate the icache to maintain coherence */
- cacheflush((long)cUnit->baseAddr,
- (long)((char *) cUnit->baseAddr + offset), 0);
+ dvmCompilerCacheFlush((long)cUnit->baseAddr,
+ (long)((char *) cUnit->baseAddr + offset), 0);
UPDATE_CODE_CACHE_PATCHES();
PROTECT_CODE_CACHE(cUnit->baseAddr, offset);
/* If applicable, mark low bit to denote thumb */
if (info->instructionSet != DALVIK_JIT_ARM)
info->codeAddress = (char*)info->codeAddress + 1;
+ /* transfer the size of the profiling code */
+ info->profileCodeSize = cUnit->profileCodeSize;
}
/*
UNPROTECT_CODE_CACHE(branchAddr, sizeof(*branchAddr));
*branchAddr = newInst;
- cacheflush((long)branchAddr, (long)branchAddr + 4, 0);
+ dvmCompilerCacheFlush((long)branchAddr, (long)branchAddr + 4, 0);
UPDATE_CODE_CACHE_PATCHES();
PROTECT_CODE_CACHE(branchAddr, sizeof(*branchAddr));
* will bring the uninitialized chaining cell to life.
*/
android_atomic_release_store((int32_t)newContent->clazz,
- (void*) &cellAddr->clazz);
- cacheflush((intptr_t) cellAddr, (intptr_t) (cellAddr+1), 0);
+ (volatile int32_t *)(void *)&cellAddr->clazz);
+ dvmCompilerCacheFlush((intptr_t) cellAddr, (intptr_t) (cellAddr+1), 0);
UPDATE_CODE_CACHE_PATCHES();
PROTECT_CODE_CACHE(cellAddr, sizeof(*cellAddr));
* trigger immediate patching and will continue to fail to match with
* a real clazz pointer.
*/
- cell->clazz = (void *) PREDICTED_CHAIN_FAKE_CLAZZ;
+ cell->clazz = (ClassObject *) PREDICTED_CHAIN_FAKE_CLAZZ;
UPDATE_CODE_CACHE_PATCHES();
PROTECT_CODE_CACHE(cell, sizeof(*cell));
}
/* Then synchronize the I/D cache */
- cacheflush((long) minAddr, (long) (maxAddr+1), 0);
+ dvmCompilerCacheFlush((long) minAddr, (long) (maxAddr+1), 0);
UPDATE_CODE_CACHE_PATCHES();
PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
highAddress = lastAddress;
}
}
- cacheflush((long)lowAddress, (long)highAddress, 0);
+ dvmCompilerCacheFlush((long)lowAddress, (long)highAddress, 0);
UPDATE_CODE_CACHE_PATCHES();
PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
(6 + (p->u.info.instructionSet == DALVIK_JIT_ARM ? 0 : 1));
}
+/* Handy function to retrieve the profile count */
+static inline JitTraceCounter_t getProfileCount(const JitEntry *entry)
+{
+ if (entry->dPC == 0 || entry->codeAddress == 0 ||
+ entry->codeAddress == dvmCompilerGetInterpretTemplate())
+ return 0;
+
+ JitTraceCounter_t **p = (JitTraceCounter_t **) getTraceBase(entry);
+
+ return **p;
+}
+
+/* Handy function to reset the profile count */
+static inline void resetProfileCount(const JitEntry *entry)
+{
+ if (entry->dPC == 0 || entry->codeAddress == 0 ||
+ entry->codeAddress == dvmCompilerGetInterpretTemplate())
+ return;
+
+ JitTraceCounter_t **p = (JitTraceCounter_t **) getTraceBase(entry);
+
+ **p = 0;
+}
+
/* Dumps profile info for a single trace */
static int dumpTraceProfile(JitEntry *p, bool silent, bool reset,
unsigned long sum)
{
ChainCellCounts* pCellCounts;
char* traceBase;
- u4* pExecutionCount;
- u4 executionCount;
+ JitTraceCounter_t count;
u2* pCellOffset;
JitTraceDescription *desc;
const Method* method;
LOGD("TRACEPROFILE 0x%08x 0 INTERPRET_ONLY 0 0", (int)traceBase);
return 0;
}
-
- pExecutionCount = (u4*) (traceBase);
- executionCount = *pExecutionCount;
+ count = getProfileCount(p);
if (reset) {
- *pExecutionCount =0;
+ resetProfileCount(p);
}
if (silent) {
- return executionCount;
+ return count;
}
pCellOffset = (u2*) (traceBase + 4);
pCellCounts = (ChainCellCounts*) ((char *)pCellOffset + *pCellOffset);
LOGD("TRACEPROFILE 0x%08x % 10d %5.2f%% [%#x(+%d), %d] %s%s;%s",
(int)traceBase,
- executionCount,
- ((float ) executionCount) / sum * 100.0,
+ count,
+ ((float ) count) / sum * 100.0,
desc->trace[0].frag.startOffset,
desc->trace[0].frag.numInsts,
addrToLine.lineNum,
* be a meta info field (only used by callsite info for now).
*/
if (!desc->trace[idx].frag.isCode) {
- const Method *method = desc->trace[idx+1].meta;
+ const Method *method = (const Method *)desc->trace[idx+1].meta;
char *methodDesc = dexProtoCopyMethodDescriptor(&method->prototype);
/* Print the callee info in the trace */
LOGD(" -> %s%s;%s", method->clazz->descriptor, method->name,
methodDesc);
}
- return executionCount;
+ return count;
}
/* Create a copy of the trace descriptor of an existing compilation */
return newCopy;
}
-/* Handy function to retrieve the profile count */
-static inline int getProfileCount(const JitEntry *entry)
-{
- if (entry->dPC == 0 || entry->codeAddress == 0 ||
- entry->codeAddress == dvmCompilerGetInterpretTemplate())
- return 0;
-
- u4 *pExecutionCount = (u4 *) getTraceBase(entry);
-
- return *pExecutionCount;
-}
-
-
/* qsort callback function */
static int sortTraceProfileCount(const void *entry1, const void *entry2)
{
- const JitEntry *jitEntry1 = entry1;
- const JitEntry *jitEntry2 = entry2;
+ const JitEntry *jitEntry1 = (const JitEntry *)entry1;
+ const JitEntry *jitEntry2 = (const JitEntry *)entry2;
- int count1 = getProfileCount(jitEntry1);
- int count2 = getProfileCount(jitEntry2);
+ JitTraceCounter_t count1 = getProfileCount(jitEntry1);
+ JitTraceCounter_t count2 = getProfileCount(jitEntry2);
return (count1 == count2) ? 0 : ((count1 > count2) ? -1 : 1);
}
dvmLockMutex(&gDvmJit.tableLock);
/* Sort the entries by descending order */
- sortedEntries = malloc(sizeof(JitEntry) * gDvmJit.jitTableSize);
+ sortedEntries = (JitEntry *)malloc(sizeof(JitEntry) * gDvmJit.jitTableSize);
if (sortedEntries == NULL)
goto done;
memcpy(sortedEntries, gDvmJit.pJitEntryTable,
*/
static ArmLIR *newLIR0(CompilationUnit *cUnit, ArmOpcode opcode)
{
- ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true);
+ ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
assert(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & NO_OPERAND));
insn->opcode = opcode;
setupResourceMasks(insn);
static ArmLIR *newLIR1(CompilationUnit *cUnit, ArmOpcode opcode,
int dest)
{
- ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true);
+ ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
assert(isPseudoOpcode(opcode) || (EncodingMap[opcode].flags & IS_UNARY_OP));
insn->opcode = opcode;
insn->operands[0] = dest;
static ArmLIR *newLIR2(CompilationUnit *cUnit, ArmOpcode opcode,
int dest, int src1)
{
- ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true);
+ ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
assert(isPseudoOpcode(opcode) ||
(EncodingMap[opcode].flags & IS_BINARY_OP));
insn->opcode = opcode;
static ArmLIR *newLIR3(CompilationUnit *cUnit, ArmOpcode opcode,
int dest, int src1, int src2)
{
- ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true);
+ ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
if (!(EncodingMap[opcode].flags & IS_TERTIARY_OP)) {
LOGE("Bad LIR3: %s[%d]",EncodingMap[opcode].name,opcode);
}
static ArmLIR *newLIR4(CompilationUnit *cUnit, ArmOpcode opcode,
int dest, int src1, int src2, int info)
{
- ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true);
+ ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
assert(isPseudoOpcode(opcode) ||
(EncodingMap[opcode].flags & IS_QUAD_OP));
insn->opcode = opcode;
{
/* Add the constant to the literal pool */
if (!inPlace) {
- ArmLIR *newValue = dvmCompilerNew(sizeof(ArmLIR), true);
+ ArmLIR *newValue = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
newValue->operands[0] = value;
newValue->generic.next = cUnit->wordList;
cUnit->wordList = (LIR *) newValue;
/* Set up the place holder to reconstruct this Dalvik PC */
if (pcrLabel == NULL) {
int dPC = (int) (cUnit->method->insns + dOffset);
- pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true);
+ pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
pcrLabel->opcode = kArmPseudoPCReconstructionCell;
pcrLabel->operands[0] = dPC;
pcrLabel->operands[1] = dOffset;
/* Insert the place holder to the growable list */
- dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel);
+ dvmInsertGrowableList(&cUnit->pcReconstructionList,
+ (intptr_t) pcrLabel);
}
/* Branch to the PC reconstruction code */
branch->generic.target = (LIR *) pcrLabel;
static void selfVerificationBranchInsert(LIR *currentLIR, ArmOpcode opcode,
int dest, int src1)
{
- ArmLIR *insn = dvmCompilerNew(sizeof(ArmLIR), true);
+ ArmLIR *insn = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
insn->opcode = opcode;
insn->operands[0] = dest;
insn->operands[1] = src1;
/* Insert branch, but defer setting of target */
ArmLIR *branch = genUnconditionalBranch(cUnit, NULL);
/* Set up the place holder to reconstruct this Dalvik PC */
- ArmLIR *pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true);
+ ArmLIR *pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
pcrLabel->opcode = kArmPseudoPCReconstructionCell;
pcrLabel->operands[0] = dPC;
pcrLabel->operands[1] = mir->offset;
/* Insert the place holder to the growable list */
- dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel);
+ dvmInsertGrowableList(&cUnit->pcReconstructionList, (intptr_t) pcrLabel);
/* Branch to the PC reconstruction code */
branch->generic.target = (LIR *) pcrLabel;
}
*/
if (pcrLabel == NULL) {
int dPC = (int) (cUnit->method->insns + mir->offset);
- pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true);
+ pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
pcrLabel->opcode = kArmPseudoPCReconstructionCell;
pcrLabel->operands[0] = dPC;
pcrLabel->operands[1] = mir->offset;
/* Insert the place holder to the growable list */
- dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel);
+ dvmInsertGrowableList(&cUnit->pcReconstructionList,
+ (intptr_t) pcrLabel);
}
/* return through lr+2 - punt to the interpreter */
isVolatile = (mir->dalvikInsn.opcode == OP_SGET_VOLATILE) ||
(mir->dalvikInsn.opcode == OP_SGET_OBJECT_VOLATILE) ||
- dvmIsVolatileField(fieldPtr);
+ dvmIsVolatileField((Field *) fieldPtr);
rlDest = dvmCompilerGetDest(cUnit, mir, 0);
rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, true);
isVolatile = (mir->dalvikInsn.opcode == OP_SPUT_VOLATILE) ||
(mir->dalvikInsn.opcode == OP_SPUT_OBJECT_VOLATILE) ||
- dvmIsVolatileField(fieldPtr);
+ dvmIsVolatileField((Field *) fieldPtr);
isSputObject = (mir->dalvikInsn.opcode == OP_SPUT_OBJECT) ||
(mir->dalvikInsn.opcode == OP_SPUT_OBJECT_VOLATILE);
* Obey the calling convention and don't mess with the register
* usage.
*/
- ClassObject *classPtr = (void*)
+ ClassObject *classPtr = (ClassObject *)
(cUnit->method->clazz->pDvmDex->pResClasses[mir->dalvikInsn.vB]);
if (classPtr == NULL) {
*/
if (pcrLabel == NULL) {
int dPC = (int) (cUnit->method->insns + mir->offset);
- pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true);
+ pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
pcrLabel->opcode = kArmPseudoPCReconstructionCell;
pcrLabel->operands[0] = dPC;
pcrLabel->operands[1] = mir->offset;
/* Insert the place holder to the growable list */
- dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel);
+ dvmInsertGrowableList(&cUnit->pcReconstructionList,
+ (intptr_t) pcrLabel);
}
/* return through lr+2 - punt to the interpreter */
addWordData(cUnit, (int) (cUnit->method->insns + offset), true);
}
-#if defined(WITH_SELF_VERIFICATION) || defined(WITH_JIT_TUNING)
/* Chaining cell for branches that branch back into the same basic block */
static void handleBackwardBranchChainingCell(CompilationUnit *cUnit,
unsigned int offset)
addWordData(cUnit, (int) (cUnit->method->insns + offset), true);
}
-#endif
/* Chaining cell for monomorphic method invocations. */
static void handleInvokeSingletonChainingCell(CompilationUnit *cUnit,
const Method *callee)
static void handleExtendedMIR(CompilationUnit *cUnit, MIR *mir)
{
int opOffset = mir->dalvikInsn.opcode - kMirOpFirst;
- char *msg = dvmCompilerNew(strlen(extendedMIROpNames[opOffset]) + 1,
- false);
+ char *msg = (char *)dvmCompilerNew(strlen(extendedMIROpNames[opOffset]) + 1,
+ false);
strcpy(msg, extendedMIROpNames[opOffset]);
newLIR1(cUnit, kArmPseudoExtended, (int) msg);
ArmLIR *bodyLabel)
{
/* Set up the place holder to reconstruct this Dalvik PC */
- ArmLIR *pcrLabel = dvmCompilerNew(sizeof(ArmLIR), true);
+ ArmLIR *pcrLabel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
pcrLabel->opcode = kArmPseudoPCReconstructionCell;
pcrLabel->operands[0] =
(int) (cUnit->method->insns + entry->startOffset);
pcrLabel->operands[1] = entry->startOffset;
/* Insert the place holder to the growable list */
- dvmInsertGrowableList(&cUnit->pcReconstructionList, pcrLabel);
+ dvmInsertGrowableList(&cUnit->pcReconstructionList, (intptr_t) pcrLabel);
/*
* Next, create two branches - one branch over to the loop body and the
* other branch to the PCR cell to punt.
*/
- ArmLIR *branchToBody = dvmCompilerNew(sizeof(ArmLIR), true);
+ ArmLIR *branchToBody = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
branchToBody->opcode = kThumbBUncond;
branchToBody->generic.target = (LIR *) bodyLabel;
setupResourceMasks(branchToBody);
cUnit->loopAnalysis->branchToBody = (LIR *) branchToBody;
- ArmLIR *branchToPCR = dvmCompilerNew(sizeof(ArmLIR), true);
+ ArmLIR *branchToPCR = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
branchToPCR->opcode = kThumbBUncond;
branchToPCR->generic.target = (LIR *) pcrLabel;
setupResourceMasks(branchToPCR);
{
/* Used to hold the labels of each block */
ArmLIR *labelList =
- dvmCompilerNew(sizeof(ArmLIR) * cUnit->numBlocks, true);
+ (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR) * cUnit->numBlocks, true);
GrowableList chainingListByType[kChainingCellGap];
int i;
dvmInitGrowableList(&chainingListByType[i], 2);
}
- BasicBlock **blockList = cUnit->blockList;
+ GrowableListIterator iterator;
+ dvmGrowableListIteratorInit(&cUnit->blockList, &iterator);
- if (cUnit->executionCount) {
- /*
- * Reserve 6 bytes at the beginning of the trace
- * +----------------------------+
- * | execution count (4 bytes) |
- * +----------------------------+
- * | chain cell offset (2 bytes)|
- * +----------------------------+
- * ...and then code to increment the execution
- * count:
- * mov r0, pc @ move adr of "mov r0,pc" + 4 to r0
- * sub r0, #10 @ back up to addr of executionCount
- * ldr r1, [r0]
- * add r1, #1
- * str r1, [r0]
- */
- newLIR1(cUnit, kArm16BitData, 0);
- newLIR1(cUnit, kArm16BitData, 0);
- cUnit->chainCellOffsetLIR =
- (LIR *) newLIR1(cUnit, kArm16BitData, CHAIN_CELL_OFFSET_TAG);
- cUnit->headerSize = 6;
- /* Thumb instruction used directly here to ensure correct size */
- newLIR2(cUnit, kThumbMovRR_H2L, r0, rpc);
- newLIR2(cUnit, kThumbSubRI8, r0, 10);
- newLIR3(cUnit, kThumbLdrRRI5, r1, r0, 0);
- newLIR2(cUnit, kThumbAddRI8, r1, 1);
- newLIR3(cUnit, kThumbStrRRI5, r1, r0, 0);
- } else {
- /* Just reserve 2 bytes for the chain cell offset */
- cUnit->chainCellOffsetLIR =
- (LIR *) newLIR1(cUnit, kArm16BitData, CHAIN_CELL_OFFSET_TAG);
- cUnit->headerSize = 2;
- }
+ /* Traces start with a profiling entry point. Generate it here */
+ cUnit->profileCodeSize = genTraceProfileEntry(cUnit);
/* Handle the content in each basic block */
- for (i = 0; i < cUnit->numBlocks; i++) {
- blockList[i]->visited = true;
+ for (i = 0; ; i++) {
MIR *mir;
+ BasicBlock *bb = (BasicBlock *) dvmGrowableListIteratorNext(&iterator);
+ if (bb == NULL) break;
- labelList[i].operands[0] = blockList[i]->startOffset;
+ labelList[i].operands[0] = bb->startOffset;
- if (blockList[i]->blockType >= kChainingCellGap) {
- if (blockList[i]->isFallThroughFromInvoke == true) {
+ if (bb->blockType >= kChainingCellGap) {
+ if (bb->isFallThroughFromInvoke == true) {
/* Align this block first since it is a return chaining cell */
newLIR0(cUnit, kArmPseudoPseudoAlign4);
}
dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[i]);
}
- if (blockList[i]->blockType == kTraceEntryBlock) {
+ if (bb->blockType == kTraceEntryBlock) {
labelList[i].opcode = kArmPseudoEntryBlock;
- if (blockList[i]->firstMIRInsn == NULL) {
+ if (bb->firstMIRInsn == NULL) {
continue;
} else {
- setupLoopEntryBlock(cUnit, blockList[i],
- &labelList[blockList[i]->fallThrough->id]);
+ setupLoopEntryBlock(cUnit, bb,
+ &labelList[bb->fallThrough->id]);
}
- } else if (blockList[i]->blockType == kTraceExitBlock) {
+ } else if (bb->blockType == kTraceExitBlock) {
labelList[i].opcode = kArmPseudoExitBlock;
goto gen_fallthrough;
- } else if (blockList[i]->blockType == kDalvikByteCode) {
+ } else if (bb->blockType == kDalvikByteCode) {
labelList[i].opcode = kArmPseudoNormalBlockLabel;
/* Reset the register state */
dvmCompilerResetRegPool(cUnit);
dvmCompilerClobberAllRegs(cUnit);
dvmCompilerResetNullCheck(cUnit);
} else {
- switch (blockList[i]->blockType) {
+ switch (bb->blockType) {
case kChainingCellNormal:
labelList[i].opcode = kArmPseudoChainingCellNormal;
/* handle the codegen later */
dvmInsertGrowableList(
- &chainingListByType[kChainingCellNormal], (void *) i);
+ &chainingListByType[kChainingCellNormal], i);
break;
case kChainingCellInvokeSingleton:
labelList[i].opcode =
kArmPseudoChainingCellInvokeSingleton;
labelList[i].operands[0] =
- (int) blockList[i]->containingMethod;
+ (int) bb->containingMethod;
/* handle the codegen later */
dvmInsertGrowableList(
- &chainingListByType[kChainingCellInvokeSingleton],
- (void *) i);
+ &chainingListByType[kChainingCellInvokeSingleton], i);
break;
case kChainingCellInvokePredicted:
labelList[i].opcode =
kArmPseudoChainingCellInvokePredicted;
/* handle the codegen later */
dvmInsertGrowableList(
- &chainingListByType[kChainingCellInvokePredicted],
- (void *) i);
+ &chainingListByType[kChainingCellInvokePredicted], i);
break;
case kChainingCellHot:
labelList[i].opcode =
kArmPseudoChainingCellHot;
/* handle the codegen later */
dvmInsertGrowableList(
- &chainingListByType[kChainingCellHot],
- (void *) i);
+ &chainingListByType[kChainingCellHot], i);
break;
case kPCReconstruction:
/* Make sure exception handling block is next */
opReg(cUnit, kOpBlx, r1);
}
break;
-#if defined(WITH_SELF_VERIFICATION) || defined(WITH_JIT_TUNING)
case kChainingCellBackwardBranch:
labelList[i].opcode =
kArmPseudoChainingCellBackwardBranch;
/* handle the codegen later */
dvmInsertGrowableList(
&chainingListByType[kChainingCellBackwardBranch],
- (void *) i);
+ i);
break;
-#endif
default:
break;
}
ArmLIR *headLIR = NULL;
- for (mir = blockList[i]->firstMIRInsn; mir; mir = mir->next) {
+ for (mir = bb->firstMIRInsn; mir; mir = mir->next) {
dvmCompilerResetRegPool(cUnit);
if (gDvmJit.disableOpt & (1 << kTrackLiveTemps)) {
case kFmt20t:
case kFmt30t:
notHandled = handleFmt10t_Fmt20t_Fmt30t(cUnit,
- mir, blockList[i], labelList);
+ mir, bb, labelList);
break;
case kFmt10x:
notHandled = handleFmt10x(cUnit, mir);
notHandled = handleFmt21s(cUnit, mir);
break;
case kFmt21t:
- notHandled = handleFmt21t(cUnit, mir, blockList[i],
- labelList);
+ notHandled = handleFmt21t(cUnit, mir, bb, labelList);
break;
case kFmt22b:
case kFmt22s:
notHandled = handleFmt22cs(cUnit, mir);
break;
case kFmt22t:
- notHandled = handleFmt22t(cUnit, mir, blockList[i],
- labelList);
+ notHandled = handleFmt22t(cUnit, mir, bb, labelList);
break;
case kFmt22x:
case kFmt32x:
break;
case kFmt3rc:
case kFmt35c:
- notHandled = handleFmt35c_3rc(cUnit, mir, blockList[i],
+ notHandled = handleFmt35c_3rc(cUnit, mir, bb,
labelList);
break;
case kFmt3rms:
case kFmt35ms:
- notHandled = handleFmt35ms_3rms(cUnit, mir,blockList[i],
+ notHandled = handleFmt35ms_3rms(cUnit, mir, bb,
labelList);
break;
case kFmt35mi:
}
}
- if (blockList[i]->blockType == kTraceEntryBlock) {
+ if (bb->blockType == kTraceEntryBlock) {
dvmCompilerAppendLIR(cUnit,
(LIR *) cUnit->loopAnalysis->branchToBody);
dvmCompilerAppendLIR(cUnit,
* Check if the block is terminated due to trace length constraint -
* insert an unconditional branch to the chaining cell.
*/
- if (blockList[i]->needFallThroughBranch) {
+ if (bb->needFallThroughBranch) {
genUnconditionalBranch(cUnit,
- &labelList[blockList[i]->fallThrough->id]);
+ &labelList[bb->fallThrough->id]);
}
}
for (j = 0; j < chainingListByType[i].numUsed; j++) {
int blockId = blockIdList[j];
+ BasicBlock *chainingBlock =
+ (BasicBlock *) dvmGrowableListGetElement(&cUnit->blockList,
+ blockId);
/* Align this chaining cell first */
newLIR0(cUnit, kArmPseudoPseudoAlign4);
dvmCompilerAppendLIR(cUnit, (LIR *) &labelList[blockId]);
- switch (blockList[blockId]->blockType) {
+ switch (chainingBlock->blockType) {
case kChainingCellNormal:
- handleNormalChainingCell(cUnit,
- blockList[blockId]->startOffset);
+ handleNormalChainingCell(cUnit, chainingBlock->startOffset);
break;
case kChainingCellInvokeSingleton:
handleInvokeSingletonChainingCell(cUnit,
- blockList[blockId]->containingMethod);
+ chainingBlock->containingMethod);
break;
case kChainingCellInvokePredicted:
handleInvokePredictedChainingCell(cUnit);
break;
case kChainingCellHot:
- handleHotChainingCell(cUnit,
- blockList[blockId]->startOffset);
+ handleHotChainingCell(cUnit, chainingBlock->startOffset);
break;
-#if defined(WITH_SELF_VERIFICATION) || defined(WITH_JIT_TUNING)
case kChainingCellBackwardBranch:
handleBackwardBranchChainingCell(cUnit,
- blockList[blockId]->startOffset);
+ chainingBlock->startOffset);
break;
-#endif
default:
- LOGE("Bad blocktype %d", blockList[blockId]->blockType);
+ LOGE("Bad blocktype %d", chainingBlock->blockType);
dvmCompilerAbort(cUnit);
}
}
#endif
}
-/* Accept the work and start compiling */
+/*
+ * Accept the work and start compiling. Returns true if compilation
+ * is attempted.
+ */
bool dvmCompilerDoWork(CompilerWorkOrder *work)
{
- bool res;
+ JitTraceDescription *desc;
+ bool isCompile;
+ bool success = true;
if (gDvmJit.codeCacheFull) {
return false;
switch (work->kind) {
case kWorkOrderTrace:
+ isCompile = true;
/* Start compilation with maximally allowed trace length */
- res = dvmCompileTrace(work->info, JIT_MAX_TRACE_LEN, &work->result,
- work->bailPtr, 0 /* no hints */);
+ desc = (JitTraceDescription *)work->info;
+ success = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result,
+ work->bailPtr, 0 /* no hints */);
break;
case kWorkOrderTraceDebug: {
bool oldPrintMe = gDvmJit.printMe;
gDvmJit.printMe = true;
+ isCompile = true;
/* Start compilation with maximally allowed trace length */
- res = dvmCompileTrace(work->info, JIT_MAX_TRACE_LEN, &work->result,
- work->bailPtr, 0 /* no hints */);
+ desc = (JitTraceDescription *)work->info;
+ success = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result,
+ work->bailPtr, 0 /* no hints */);
gDvmJit.printMe = oldPrintMe;
break;
}
+ case kWorkOrderProfileMode:
+ dvmJitChangeProfileMode((TraceProfilingModes)work->info);
+ isCompile = false;
+ break;
default:
- res = false;
+ isCompile = false;
LOGE("Jit: unknown work order type");
assert(0); // Bail if debug build, discard otherwise
}
- return res;
+ if (!success)
+ work->result.codeAddress = NULL;
+ return isCompile;
}
/* Architectural-specific debugging helpers go here */
/* The store can be sunk for at least one cycle */
if (sinkDistance != 0) {
ArmLIR *newStoreLIR =
- dvmCompilerNew(sizeof(ArmLIR), true);
+ (ArmLIR *)dvmCompilerNew(sizeof(ArmLIR), true);
*newStoreLIR = *thisLIR;
newStoreLIR->age = cUnit->optRound;
/*
/* The load can be hoisted for at least one cycle */
if (hoistDistance != 0) {
ArmLIR *newLoadLIR =
- dvmCompilerNew(sizeof(ArmLIR), true);
+ (ArmLIR *)dvmCompilerNew(sizeof(ArmLIR), true);
*newLoadLIR = *thisLIR;
newLoadLIR->age = cUnit->optRound;
/*
/* The store can be hoisted for at least one cycle */
if (hoistDistance != 0) {
ArmLIR *newLoadLIR =
- dvmCompilerNew(sizeof(ArmLIR), true);
+ (ArmLIR *)dvmCompilerNew(sizeof(ArmLIR), true);
*newLoadLIR = *thisLIR;
newLoadLIR->age = cUnit->optRound;
/*
if (dataTarget == NULL) {
dataTarget = addWordData(cUnit, value, false);
}
- ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
+ ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
loadPcRel->opcode = kThumbLdrPcRel;
loadPcRel->generic.target = (LIR *) dataTarget;
loadPcRel->operands[0] = tDest;
{
ArmLIR* res;
ArmOpcode opcode;
- res = dvmCompilerNew(sizeof(ArmLIR), true);
+ res = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
if (LOWREG(rDest) && LOWREG(rSrc))
opcode = kThumbMovRR;
else if (!LOWREG(rDest) && !LOWREG(rSrc))
*/
/*
+ * Reserve 6 bytes at the beginning of the trace
+ * +----------------------------+
+ * | prof count addr (4 bytes) |
+ * +----------------------------+
+ * | chain cell offset (2 bytes)|
+ * +----------------------------+
+ *
+ * ...and then code to increment the execution
+ *
+ * For continuous profiling (12 bytes):
+ *
+ * mov r0, pc @ move adr of "mov r0,pc" + 4 to r0
+ * sub r0, #10 @ back up to addr prof count pointer
+ * ldr r0, [r0] @ get address of counter
+ * ldr r1, [r0]
+ * add r1, #1
+ * str r1, [r0]
+ *
+ * For periodic profiling (4 bytes):
+ * call TEMPLATE_PERIODIC_PROFILING
+ *
+ * and return the size (in bytes) of the generated code.
+ */
+
+static int genTraceProfileEntry(CompilationUnit *cUnit)
+{
+ intptr_t addr = (intptr_t)dvmJitNextTraceCounter();
+ assert(__BYTE_ORDER == __LITTLE_ENDIAN);
+ newLIR1(cUnit, kArm16BitData, addr & 0xffff);
+ newLIR1(cUnit, kArm16BitData, (addr >> 16) & 0xffff);
+ cUnit->chainCellOffsetLIR =
+ (LIR *) newLIR1(cUnit, kArm16BitData, CHAIN_CELL_OFFSET_TAG);
+ cUnit->headerSize = 6;
+ if ((gDvmJit.profileMode == kTraceProfilingContinuous) ||
+ (gDvmJit.profileMode == kTraceProfilingDisabled)) {
+ /* Thumb instruction used directly here to ensure correct size */
+ newLIR2(cUnit, kThumbMovRR_H2L, r0, rpc);
+ newLIR2(cUnit, kThumbSubRI8, r0, 10);
+ newLIR3(cUnit, kThumbLdrRRI5, r0, r0, 0);
+ newLIR3(cUnit, kThumbLdrRRI5, r1, r0, 0);
+ newLIR2(cUnit, kThumbAddRI8, r1, 1);
+ newLIR3(cUnit, kThumbStrRRI5, r1, r0, 0);
+ return 12;
+ } else {
+ int opcode = TEMPLATE_PERIODIC_PROFILING;
+ newLIR2(cUnit, kThumbBlx1,
+ (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+ (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+ newLIR2(cUnit, kThumbBlx2,
+ (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+ (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+ return 4;
+ }
+}
+
+/*
* Perform a "reg cmp imm" operation and jump to the PCR region if condition
* satisfies.
*/
void dvmCompilerInitializeRegAlloc(CompilationUnit *cUnit)
{
int numTemps = sizeof(coreTemps)/sizeof(int);
- RegisterPool *pool = dvmCompilerNew(sizeof(*pool), true);
+ RegisterPool *pool = (RegisterPool *) dvmCompilerNew(sizeof(*pool), true);
cUnit->regPool = pool;
pool->numCoreTemps = numTemps;
- pool->coreTemps =
+ pool->coreTemps = (RegisterInfo *)
dvmCompilerNew(numTemps * sizeof(*pool->coreTemps), true);
pool->numFPTemps = 0;
pool->FPTemps = NULL;
- pool->numCoreRegs = 0;
- pool->coreRegs = NULL;
- pool->numFPRegs = 0;
- pool->FPRegs = NULL;
dvmCompilerInitPool(pool->coreTemps, coreTemps, pool->numCoreTemps);
dvmCompilerInitPool(pool->FPTemps, NULL, 0);
- dvmCompilerInitPool(pool->coreRegs, NULL, 0);
- dvmCompilerInitPool(pool->FPRegs, NULL, 0);
pool->nullCheckedRegs =
dvmCompilerAllocBitVector(cUnit->numSSARegs, false);
}
if (dataTarget == NULL) {
dataTarget = addWordData(cUnit, value, false);
}
- ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
+ ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
loadPcRel->opcode = kThumb2Vldrs;
loadPcRel->generic.target = (LIR *) dataTarget;
loadPcRel->operands[0] = rDest;
if (dataTarget == NULL) {
dataTarget = addWordData(cUnit, value, false);
}
- ArmLIR *loadPcRel = dvmCompilerNew(sizeof(ArmLIR), true);
+ ArmLIR *loadPcRel = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
loadPcRel->opcode = kThumb2LdrPcRel12;
loadPcRel->generic.target = (LIR *) dataTarget;
loadPcRel->operands[0] = rDest;
static ArmLIR *fpRegCopy(CompilationUnit *cUnit, int rDest, int rSrc)
{
- ArmLIR* res = dvmCompilerNew(sizeof(ArmLIR), true);
+ ArmLIR* res = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
res->operands[0] = rDest;
res->operands[1] = rSrc;
if (rDest == rSrc) {
ArmOpcode opcode;
if (FPREG(rDest) || FPREG(rSrc))
return fpRegCopy(cUnit, rDest, rSrc);
- res = dvmCompilerNew(sizeof(ArmLIR), true);
+ res = (ArmLIR *) dvmCompilerNew(sizeof(ArmLIR), true);
if (LOWREG(rDest) && LOWREG(rSrc))
opcode = kThumbMovRR;
else if (!LOWREG(rDest) && !LOWREG(rSrc))
*/
/*
- * This file contains codegen for the Thumb ISA and is intended to be
+ * This file contains codegen for the Thumb2 ISA and is intended to be
* includes by:
*
* Codegen-$(TARGET_ARCH_VARIANT).c
*
*/
+/*
+ * Reserve 6 bytes at the beginning of the trace
+ * +----------------------------+
+ * | prof count addr (4 bytes) |
+ * +----------------------------+
+ * | chain cell offset (2 bytes)|
+ * +----------------------------+
+ *
+ * ...and then code to increment the execution
+ *
+ * For continuous profiling (10 bytes)
+ * ldr r0, [pc-8] @ get prof count addr [4 bytes]
+ * ldr r1, [r0] @ load counter [2 bytes]
+ * add r1, #1 @ increment [2 bytes]
+ * str r1, [r0] @ store [2 bytes]
+ *
+ * For periodic profiling (4 bytes)
+ * call TEMPLATE_PERIODIC_PROFILING
+ *
+ * and return the size (in bytes) of the generated code.
+ */
+
+static int genTraceProfileEntry(CompilationUnit *cUnit)
+{
+ intptr_t addr = (intptr_t)dvmJitNextTraceCounter();
+ assert(__BYTE_ORDER == __LITTLE_ENDIAN);
+ newLIR1(cUnit, kArm16BitData, addr & 0xffff);
+ newLIR1(cUnit, kArm16BitData, (addr >> 16) & 0xffff);
+ cUnit->chainCellOffsetLIR =
+ (LIR *) newLIR1(cUnit, kArm16BitData, CHAIN_CELL_OFFSET_TAG);
+ cUnit->headerSize = 6;
+ if ((gDvmJit.profileMode == kTraceProfilingContinuous) ||
+ (gDvmJit.profileMode == kTraceProfilingDisabled)) {
+ /* Thumb[2] instruction used directly here to ensure correct size */
+ newLIR2(cUnit, kThumb2LdrPcReln12, r0, 8);
+ newLIR3(cUnit, kThumbLdrRRI5, r1, r0, 0);
+ newLIR2(cUnit, kThumbAddRI8, r1, 1);
+ newLIR3(cUnit, kThumbStrRRI5, r1, r0, 0);
+ return 10;
+ } else {
+ int opcode = TEMPLATE_PERIODIC_PROFILING;
+ newLIR2(cUnit, kThumbBlx1,
+ (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+ (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+ newLIR2(cUnit, kThumbBlx2,
+ (int) gDvmJit.codeCache + templateEntryOffsets[opcode],
+ (int) gDvmJit.codeCache + templateEntryOffsets[opcode]);
+ return 4;
+ }
+}
+
static void genNegFloat(CompilationUnit *cUnit, RegLocation rlDest,
RegLocation rlSrc)
{
{
int numTemps = sizeof(coreTemps)/sizeof(int);
int numFPTemps = sizeof(fpTemps)/sizeof(int);
- RegisterPool *pool = dvmCompilerNew(sizeof(*pool), true);
+ RegisterPool *pool = (RegisterPool *)dvmCompilerNew(sizeof(*pool), true);
cUnit->regPool = pool;
pool->numCoreTemps = numTemps;
- pool->coreTemps =
+ pool->coreTemps = (RegisterInfo *)
dvmCompilerNew(numTemps * sizeof(*cUnit->regPool->coreTemps), true);
pool->numFPTemps = numFPTemps;
- pool->FPTemps =
+ pool->FPTemps = (RegisterInfo *)
dvmCompilerNew(numFPTemps * sizeof(*cUnit->regPool->FPTemps), true);
- pool->numCoreRegs = 0;
- pool->coreRegs = NULL;
- pool->numFPRegs = 0;
- pool->FPRegs = NULL;
dvmCompilerInitPool(pool->coreTemps, coreTemps, pool->numCoreTemps);
dvmCompilerInitPool(pool->FPTemps, fpTemps, pool->numFPTemps);
- dvmCompilerInitPool(pool->coreRegs, NULL, 0);
- dvmCompilerInitPool(pool->FPRegs, NULL, 0);
pool->nullCheckedRegs =
dvmCompilerAllocBitVector(cUnit->numSSARegs, false);
}
void dvmCompilerCodegenDump(CompilationUnit *cUnit)
{
}
+
+/* Target-specific cache flushing (not needed for x86 */
+int dvmCompilerCacheFlush(long start, long end, long flags)
+{
+ return 0;
+}
#include "../../CompilerInternals.h"
#include "X86LIR.h"
#include "Codegen.h"
-#include <unistd.h> /* for cacheflush */
#include <sys/mman.h> /* for protection change */
#define MAX_ASSEMBLER_RETRIES 10
#endif
/*
- * FIXME - redo for x86
- *
* Translation layout in the code cache. Note that the codeAddress pointer
* in JitTable will point directly to the code body (field codeAddress). The
* chain cell offset codeAddress - 2, and (if present) executionCount is at
* | . .
* | | |
* | +----------------------------+
- * | | Chaining Cells | -> 12/16 bytes each, must be 4 byte aligned
+ * | | Chaining Cells | -> 16 bytes each, 8 byte aligned
* | . .
* | . .
* | | |
* | |
* +----------------------------+
* | Literal pool | -> 4-byte aligned, variable size
- * . .
- * . .
+ * . . Note: for x86 literals will
+ * . . generally appear inline.
* | |
* +----------------------------+
*
* applicable directory below this one.
*/
+extern X86LIR *loadConstant(CompilationUnit *cUnit, int rDest, int value);
+extern X86LIR *loadWordDisp(CompilationUnit *cUnit, int rBase,
+ int displacement, int rDest);
+extern void dvmCompilerFlushAllRegs(CompilationUnit *cUnit);
+extern void storeWordDisp(CompilationUnit *cUnit, int rBase,
+ int displacement, int rSrc);
+extern X86LIR *opReg(CompilationUnit *cUnit, OpKind op, int rDestSrc);
+
static int opcodeCoverage[kNumPackedOpcodes];
static intptr_t templateEntryOffsets[TEMPLATE_LAST_MARK];
+#if 0 // Avoid compiler warnings when x86 disabled during development
+/*
+ * Bail to the interpreter. Will not return to this trace.
+ * On entry, rPC must be set correctly.
+ */
+static void genPuntToInterp(CompilationUnit *cUnit, unsigned int offset)
+{
+ dvmCompilerFlushAllRegs(cUnit);
+ loadConstant(cUnit, rPC, (int)(cUnit->method->insns + offset));
+ loadWordDisp(cUnit, rEBP, 0, rECX); // Get glue
+ loadWordDisp(cUnit, rECX,
+ offsetof(InterpState, jitToInterpEntries.dvmJitToInterpPunt),
+ rEAX);
+ opReg(cUnit, kOpUncondBr, rEAX);
+}
+
+static void genInterpSingleStep(CompilationUnit *cUnit, MIR *mir)
+{
+ int flags = dexGetFlagsFromOpcode(mir->dalvikInsn.opcode);
+ int flagsToCheck = kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn |
+ kInstrCanThrow;
+
+ //If already optimized out, just ignore
+ if (mir->dalvikInsn.opcode == OP_NOP)
+ return;
+
+ //Ugly, but necessary. Flush all Dalvik regs so Interp can find them
+ dvmCompilerFlushAllRegs(cUnit);
+
+ if ((mir->next == NULL) || (flags & flagsToCheck)) {
+ genPuntToInterp(cUnit, mir->offset);
+ return;
+ }
+ int entryAddr = offsetof(InterpState,
+ jitToInterpEntries.dvmJitToInterpSingleStep);
+ loadWordDisp(cUnit, rEBP, 0, rECX); // Get glue
+ loadWordDisp(cUnit, rECX, entryAddr, rEAX); // rEAX<- entry address
+ /* rPC = dalvik pc */
+ loadConstant(cUnit, rPC, (int) (cUnit->method->insns + mir->offset));
+ /* rECX = dalvik pc of following instruction */
+ loadConstant(cUnit, rECX, (int) (cUnit->method->insns + mir->next->offset));
+ /* Pass on the stack */
+ storeWordDisp(cUnit, rESP, OUT_ARG0, rECX);
+ opReg(cUnit, kOpCall, rEAX);
+}
+#endif
+
/*
* The following are the first-level codegen routines that analyze the format
* of each bytecode then either dispatch special purpose codegen routines
/* Accept the work and start compiling */
bool dvmCompilerDoWork(CompilerWorkOrder *work)
{
+ JitTraceDescription *desc;
bool res;
if (gDvmJit.codeCacheFull) {
switch (work->kind) {
case kWorkOrderTrace:
/* Start compilation with maximally allowed trace length */
- res = dvmCompileTrace(work->info, JIT_MAX_TRACE_LEN, &work->result,
+ desc = (JitTraceDescription *)work->info;
+ res = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result,
work->bailPtr, 0 /* no hints */);
break;
case kWorkOrderTraceDebug: {
bool oldPrintMe = gDvmJit.printMe;
gDvmJit.printMe = true;
/* Start compilation with maximally allowed trace length */
- res = dvmCompileTrace(work->info, JIT_MAX_TRACE_LEN, &work->result,
+ desc = (JitTraceDescription *)work->info;
+ res = dvmCompileTrace(desc, JIT_MAX_TRACE_LEN, &work->result,
work->bailPtr, 0 /* no hints */);
gDvmJit.printMe = oldPrintMe;
break;
* esp is native SP
*
* For interpreter:
- * edx is Dalvik PC (rPC)
+ * edi is Dalvik PC (rPC)
* ebx is rINST
*
* For JIT:
int numFPTemps;
RegisterInfo *FPTemps;
int nextFPTemp;
- int numCoreRegs;
- RegisterInfo *coreRegs;
- int numFPRegs;
- RegisterInfo *FPRegs;
} RegisterPool;
typedef enum OpSize {
typedef enum OpKind {
kOpMov,
- kOpMvn,
kOpCmp,
kOpLsl,
kOpLsr,
kOpAdc,
kOpSub,
kOpSbc,
- kOpRsub,
kOpMul,
kOpDiv,
kOpRem,
- kOpBic,
- kOpCmn,
kOpTst,
- kOpBkpt,
- kOpBlx,
+ kOpCall,
kOpPush,
kOpPop,
kOp2Char,
kOpUncondBr,
} OpKind;
+#define FP_REG_OFFSET 8
+
+typedef enum NativeRegisterPool {
+ rEAX = 0,
+ rECX = 1,
+ rEDX = 2,
+ rEBX = 3,
+ rESP = 4,
+ rEBP = 5,
+ rESI = 6,
+ rEDI = 7,
+ rXMM0 = 0 + FP_REG_OFFSET,
+ rXMM1 = 1 + FP_REG_OFFSET,
+ rXMM2 = 2 + FP_REG_OFFSET,
+ rXMM3 = 3 + FP_REG_OFFSET,
+ rXMM4 = 4 + FP_REG_OFFSET,
+ rXMM5 = 5 + FP_REG_OFFSET,
+ rXMM6 = 6 + FP_REG_OFFSET,
+ rXMM7 = 7 + FP_REG_OFFSET,
+} NativeRegisterPool;
+
+#define rPC rEDI
+#define rFP rESI
+#define rINST rEBX
+
+#define OUT_ARG0 0
+#define OUT_ARG1 4
+#define OUT_ARG2 8
+#define OUT_ARG3 12
+#define OUT_ARG4 16
+
typedef struct X86LIR {
LIR generic;
//X86Opcode opcode;
JIT_TEMPLATE(INTERPRET)
JIT_TEMPLATE(MONITOR_ENTER)
JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
+JIT_TEMPLATE(PERIODIC_PROFILING)
--- /dev/null
+ /*
+ * Increment profile counter for this trace, and decrement
+ * sample counter. If sample counter goes below zero, turn
+ * off profiling.
+ *
+ * On entry
+ * (lr-11) is address of pointer to counter. Note: the counter
+ * actually exists 10 bytes before the return target, but because
+ * we are arriving from thumb mode, lr will have its low bit set.
+ */
+ ldr r0, [lr,#-11]
+ ldr r1, [rGLUE, #offGlue_pProfileCountdown]
+ ldr r2, [r0] @ get counter
+ ldr r3, [r1] @ get countdown timer
+ add r2, #1
+ subs r2, #1
+ blt .L${opcode}_disable_profiling
+ str r2, [r0]
+ str r3, [r1]
+ bx lr
+
+.L${opcode}_disable_profiling:
+ mov r4, lr @ preserve lr
+ ldr r0, .LdvmJitTraceProfilingOff
+ blx r0
+ bx r4
JIT_TEMPLATE(INTERPRET)
JIT_TEMPLATE(MONITOR_ENTER)
JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
+JIT_TEMPLATE(PERIODIC_PROFILING)
.word dvmMterpCommonExceptionThrown
.LdvmLockObject:
.word dvmLockObject
+.LdvmJitTraceProfilingOff:
+ .word dvmJitTraceProfilingOff
#if defined(WITH_JIT_TUNING)
.LdvmICHitCount:
.word gDvmICHitCount
JIT_TEMPLATE(INTERPRET)
JIT_TEMPLATE(MONITOR_ENTER)
JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
+JIT_TEMPLATE(PERIODIC_PROFILING)
JIT_TEMPLATE(INTERPRET)
JIT_TEMPLATE(MONITOR_ENTER)
JIT_TEMPLATE(MONITOR_ENTER_DEBUG)
+JIT_TEMPLATE(PERIODIC_PROFILING)
op TEMPLATE_INTERPRET armv5te
op TEMPLATE_MONITOR_ENTER armv5te
op TEMPLATE_MONITOR_ENTER_DEBUG armv5te
+ op TEMPLATE_PERIODIC_PROFILING armv5te
op-end
op TEMPLATE_INTERPRET armv5te
op TEMPLATE_MONITOR_ENTER armv5te
op TEMPLATE_MONITOR_ENTER_DEBUG armv5te
+ op TEMPLATE_PERIODIC_PROFILING armv5te
op-end
# "helper" code for C; include if you use any of the C stubs (this generates
op TEMPLATE_INTERPRET armv5te
op TEMPLATE_MONITOR_ENTER armv5te
op TEMPLATE_MONITOR_ENTER_DEBUG armv5te
+ op TEMPLATE_PERIODIC_PROFILING armv5te
op-end
# "helper" code for C; include if you use any of the C stubs (this generates
/*
- * TODO: figure out how best to do this on x86, as we don't have
- * an lr equivalent and probably don't want to push.
+ * This handler is a bit odd - it may be called via chaining or
+ * from static code and is expected to cause control to flow
+ * to the interpreter. The problem is where to find the Dalvik
+ * PC of the next instruction. When called via chaining, the dPC
+ * will be located at *rp. When called from static code, rPC is
+ * valid and rp is a real return pointer (that should be ignored).
+ * The Arm target deals with this by using the link register as
+ * a flag. If it is zero, we know we were called from static code.
+ * If non-zero, it points to the chain cell containing dPC.
+ * For x86, we'll infer the source by looking where rp points.
+ * If it points to anywhere within the code cache, we'll assume
+ * we got here via chaining. Otherwise, we'll assume rPC is valid.
*
- * This handler transfers control to the interpeter without performing
- * any lookups. It may be called either as part of a normal chaining
- * operation, or from the transition code in header.S. We distinquish
- * the two cases by looking at the link register. If called from a
- * translation chain, it will point to the chaining Dalvik PC -3.
* On entry:
- * lr - if NULL:
- * r1 - the Dalvik PC to begin interpretation.
- * else
- * [lr, #3] contains Dalvik PC to begin interpretation
- * rGLUE - pointer to interpState
- * rFP - Dalvik frame pointer
- *
- *cmp lr, #0
- *ldrne r1,[lr, #3]
- *ldr r2, .LinterpPunt
- *mov r0, r1 @ set Dalvik PC
- *bx r2
- *@ doesn't return
+ * (TOS)<- return pointer or pointer to dPC
*/
+ movl rGLUE,%ecx
+ movl $$.LinterpPunt,%edx
+ pop %eax
+ cmpl %eax,offGlue_jitCacheEnd(%ecx)
+ ja 1f
+ cmpl %eax,offGlue_jitCacheStart(%ecx)
+ jb 1f
+ movl %eax,rPC
+1:
+ jmp *(%edx)
.LinterpPunt:
.long dvmJitToInterpPunt
.text
.align 4
-/*
- * FIXME - need a cacheflush for x86
- */
- .global cacheflush
-cacheflush:
- movl $$0xdeadf0f0, %eax
- call *%eax
-
.global dmvCompilerTemplateEnd
dmvCompilerTemplateEnd:
#if defined(WITH_JIT)
+/* Subset of defines from mterp/x86/header.S */
+#define rGLUE (%ebp)
+#define rPC %esi
+#define rFP %edi
+#define rINST %ebx
+
/*
* This is a #include, not a %include, because we want the C pre-processor
* to expand the macros into assembler assignment statements.
* ===========================================================================
*/
-/*
- * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
- * Jump to subroutine.
- *
- * May modify IP and LR.
- */
-.macro LDR_PC_LR source
- mov lr, pc
- ldr pc, \source
-.endm
-
.global dvmCompilerTemplateStart
.type dvmCompilerTemplateStart, %function
stmfd sp!, {r0-r2,lr} @ preserve live registers
mov r0, r6
@ r0=rGlue
- LDR_PC_LR ".LdvmFastJavaMethodTraceExit"
+ mov lr, pc
+ ldr pc, .LdvmFastJavaMethodTraceExit
ldmfd sp!, {r0-r2,lr} @ restore live registers
#endif
SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old)
stmfd sp!, {r0-r3} @ preserve r0-r3
mov r1, r6
@ r0=methodToCall, r1=rGlue
- LDR_PC_LR ".LdvmFastMethodTraceEnter"
+ mov lr, pc
+ ldr pc, .LdvmFastMethodTraceEnter
ldmfd sp!, {r0-r3} @ restore r0-r3
#endif
stmfd sp!, {r0-r2,lr} @ preserve clobbered live registers
mov r1, r6
@ r0=methodToCall, r1=rGlue
- LDR_PC_LR ".LdvmFastMethodTraceEnter"
+ mov lr, pc
+ ldr pc, .LdvmFastMethodTraceEnter
ldmfd sp!, {r0-r2,lr} @ restore registers
#endif
mov r0, r2
mov r1, r6
@ r0=JNIMethod, r1=rGlue
- LDR_PC_LR ".LdvmFastMethodTraceEnter"
+ mov lr, pc
+ ldr pc, .LdvmFastMethodTraceEnter
ldmfd sp!, {r0-r3} @ restore r0-r3
#endif
#if defined(WITH_INLINE_PROFILING)
ldmfd sp!, {r0-r1} @ restore r2 and r6
@ r0=JNIMethod, r1=rGlue
- LDR_PC_LR ".LdvmFastNativeMethodTraceExit"
+ mov lr, pc
+ ldr pc, .LdvmFastNativeMethodTraceExit
#endif
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
#endif
ldr pc, .LdvmJitToInterpNoChain
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_PERIODIC_PROFILING
+dvmCompiler_TEMPLATE_PERIODIC_PROFILING:
+/* File: armv5te/TEMPLATE_PERIODIC_PROFILING.S */
+ /*
+ * Increment profile counter for this trace, and decrement
+ * sample counter. If sample counter goes below zero, turn
+ * off profiling.
+ *
+ * On entry
+ * (lr-11) is address of pointer to counter. Note: the counter
+ * actually exists 10 bytes before the return target, but because
+ * we are arriving from thumb mode, lr will have its low bit set.
+ */
+ ldr r0, [lr,#-11]
+ ldr r1, [rGLUE, #offGlue_pProfileCountdown]
+ ldr r2, [r0] @ get counter
+ ldr r3, [r1] @ get countdown timer
+ add r2, #1
+ subs r2, #1
+ blt .LTEMPLATE_PERIODIC_PROFILING_disable_profiling
+ str r2, [r0]
+ str r3, [r1]
+ bx lr
+
+.LTEMPLATE_PERIODIC_PROFILING_disable_profiling:
+ mov r4, lr @ preserve lr
+ ldr r0, .LdvmJitTraceProfilingOff
+ blx r0
+ bx r4
+
.size dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
/* File: armv5te/footer.S */
/*
stmfd sp!, {r0-r3}
mov r0, r2
mov r1, r6
- LDR_PC_LR ".LdvmFastMethodTraceEnter"
+ mov lr, pc
+ ldr pc, .LdvmFastMethodTraceEnter
ldmfd sp!, {r0-r3}
#endif
- LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+ mov lr, pc
+ ldr pc, [r2, #offMethod_nativeFunc]
#if defined(WITH_INLINE_PROFILING)
ldmfd sp!, {r0-r1}
- LDR_PC_LR ".LdvmFastNativeMethodTraceExit"
+ mov lr, pc
+ ldr pc, .LdvmFastNativeMethodTraceExit
#endif
@ Refresh Jit's on/off status
ldr r3, [rGLUE, #offGlue_ppJitProfTable]
.word dvmMterpCommonExceptionThrown
.LdvmLockObject:
.word dvmLockObject
+.LdvmJitTraceProfilingOff:
+ .word dvmJitTraceProfilingOff
#if defined(WITH_JIT_TUNING)
.LdvmICHitCount:
.word gDvmICHitCount
* ===========================================================================
*/
-/*
- * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
- * Jump to subroutine.
- *
- * May modify IP and LR.
- */
-.macro LDR_PC_LR source
- mov lr, pc
- ldr pc, \source
-.endm
-
.global dvmCompilerTemplateStart
.type dvmCompilerTemplateStart, %function
stmfd sp!, {r0-r2,lr} @ preserve live registers
mov r0, r6
@ r0=rGlue
- LDR_PC_LR ".LdvmFastJavaMethodTraceExit"
+ mov lr, pc
+ ldr pc, .LdvmFastJavaMethodTraceExit
ldmfd sp!, {r0-r2,lr} @ restore live registers
#endif
SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old)
stmfd sp!, {r0-r3} @ preserve r0-r3
mov r1, r6
@ r0=methodToCall, r1=rGlue
- LDR_PC_LR ".LdvmFastMethodTraceEnter"
+ mov lr, pc
+ ldr pc, .LdvmFastMethodTraceEnter
ldmfd sp!, {r0-r3} @ restore r0-r3
#endif
stmfd sp!, {r0-r2,lr} @ preserve clobbered live registers
mov r1, r6
@ r0=methodToCall, r1=rGlue
- LDR_PC_LR ".LdvmFastMethodTraceEnter"
+ mov lr, pc
+ ldr pc, .LdvmFastMethodTraceEnter
ldmfd sp!, {r0-r2,lr} @ restore registers
#endif
mov r0, r2
mov r1, r6
@ r0=JNIMethod, r1=rGlue
- LDR_PC_LR ".LdvmFastMethodTraceEnter"
+ mov lr, pc
+ ldr pc, .LdvmFastMethodTraceEnter
ldmfd sp!, {r0-r3} @ restore r0-r3
#endif
#if defined(WITH_INLINE_PROFILING)
ldmfd sp!, {r0-r1} @ restore r2 and r6
@ r0=JNIMethod, r1=rGlue
- LDR_PC_LR ".LdvmFastNativeMethodTraceExit"
+ mov lr, pc
+ ldr pc, .LdvmFastNativeMethodTraceExit
#endif
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
/* op vAA, vBB, vCC */
push {r0-r3} @ save operands
mov r11, lr @ save return address
- LDR_PC_LR ".L__aeabi_cdcmple" @ PIC way of "bl __aeabi_cdcmple"
+ mov lr, pc
+ ldr pc, .L__aeabi_cdcmple @ PIC way of "bl __aeabi_cdcmple"
bhi .LTEMPLATE_CMPG_DOUBLE_gt_or_nan @ C set and Z clear, disambiguate
mvncc r0, #0 @ (less than) r1<- -1
moveq r0, #0 @ (equal) r1<- 0, trumps less than
.LTEMPLATE_CMPG_DOUBLE_gt_or_nan:
pop {r2-r3} @ restore operands in reverse order
pop {r0-r1} @ restore operands in reverse order
- LDR_PC_LR ".L__aeabi_cdcmple" @ r0<- Z set if eq, C clear if <
+ mov lr, pc
+ ldr pc, .L__aeabi_cdcmple @ r0<- Z set if eq, C clear if <
movcc r0, #1 @ (greater than) r1<- 1
bxcc r11
mov r0, #1 @ r1<- 1 or -1 for NaN
/* op vAA, vBB, vCC */
push {r0-r3} @ save operands
mov r11, lr @ save return address
- LDR_PC_LR ".L__aeabi_cdcmple" @ PIC way of "bl __aeabi_cdcmple"
+ mov lr, pc
+ ldr pc, .L__aeabi_cdcmple @ PIC way of "bl __aeabi_cdcmple"
bhi .LTEMPLATE_CMPL_DOUBLE_gt_or_nan @ C set and Z clear, disambiguate
mvncc r0, #0 @ (less than) r1<- -1
moveq r0, #0 @ (equal) r1<- 0, trumps less than
.LTEMPLATE_CMPL_DOUBLE_gt_or_nan:
pop {r2-r3} @ restore operands in reverse order
pop {r0-r1} @ restore operands in reverse order
- LDR_PC_LR ".L__aeabi_cdcmple" @ r0<- Z set if eq, C clear if <
+ mov lr, pc
+ ldr pc, .L__aeabi_cdcmple @ r0<- Z set if eq, C clear if <
movcc r0, #1 @ (greater than) r1<- 1
bxcc r11
mvn r0, #0 @ r1<- 1 or -1 for NaN
mov r9, r0 @ Save copies - we may need to redo
mov r10, r1
mov r11, lr @ save return address
- LDR_PC_LR ".L__aeabi_cfcmple" @ cmp <=: C clear if <, Z set if eq
+ mov lr, pc
+ ldr pc, .L__aeabi_cfcmple @ cmp <=: C clear if <, Z set if eq
bhi .LTEMPLATE_CMPG_FLOAT_gt_or_nan @ C set and Z clear, disambiguate
mvncc r0, #0 @ (less than) r0<- -1
moveq r0, #0 @ (equal) r0<- 0, trumps less than
.LTEMPLATE_CMPG_FLOAT_gt_or_nan:
mov r0, r10 @ restore in reverse order
mov r1, r9
- LDR_PC_LR ".L__aeabi_cfcmple" @ r0<- Z set if eq, C clear if <
+ mov lr, pc
+ ldr pc, .L__aeabi_cfcmple @ r0<- Z set if eq, C clear if <
movcc r0, #1 @ (greater than) r1<- 1
bxcc r11
mov r0, #1 @ r1<- 1 or -1 for NaN
mov r9, r0 @ Save copies - we may need to redo
mov r10, r1
mov r11, lr @ save return address
- LDR_PC_LR ".L__aeabi_cfcmple" @ cmp <=: C clear if <, Z set if eq
+ mov lr, pc
+ ldr pc, .L__aeabi_cfcmple @ cmp <=: C clear if <, Z set if eq
bhi .LTEMPLATE_CMPL_FLOAT_gt_or_nan @ C set and Z clear, disambiguate
mvncc r0, #0 @ (less than) r0<- -1
moveq r0, #0 @ (equal) r0<- 0, trumps less than
.LTEMPLATE_CMPL_FLOAT_gt_or_nan:
mov r0, r10 @ restore in reverse order
mov r1, r9
- LDR_PC_LR ".L__aeabi_cfcmple" @ r0<- Z set if eq, C clear if <
+ mov lr, pc
+ ldr pc, .L__aeabi_cfcmple @ r0<- Z set if eq, C clear if <
movcc r0, #1 @ (greater than) r1<- 1
bxcc r11
mvn r0, #0 @ r1<- 1 or -1 for NaN
#endif
ldr pc, .LdvmJitToInterpNoChain
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_PERIODIC_PROFILING
+dvmCompiler_TEMPLATE_PERIODIC_PROFILING:
+/* File: armv5te/TEMPLATE_PERIODIC_PROFILING.S */
+ /*
+ * Increment profile counter for this trace, and decrement
+ * sample counter. If sample counter goes below zero, turn
+ * off profiling.
+ *
+ * On entry
+ * (lr-11) is address of pointer to counter. Note: the counter
+ * actually exists 10 bytes before the return target, but because
+ * we are arriving from thumb mode, lr will have its low bit set.
+ */
+ ldr r0, [lr,#-11]
+ ldr r1, [rGLUE, #offGlue_pProfileCountdown]
+ ldr r2, [r0] @ get counter
+ ldr r3, [r1] @ get countdown timer
+ add r2, #1
+ subs r2, #1
+ blt .LTEMPLATE_PERIODIC_PROFILING_disable_profiling
+ str r2, [r0]
+ str r3, [r1]
+ bx lr
+
+.LTEMPLATE_PERIODIC_PROFILING_disable_profiling:
+ mov r4, lr @ preserve lr
+ ldr r0, .LdvmJitTraceProfilingOff
+ blx r0
+ bx r4
+
.size dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
/* File: armv5te/footer.S */
/*
stmfd sp!, {r0-r3}
mov r0, r2
mov r1, r6
- LDR_PC_LR ".LdvmFastMethodTraceEnter"
+ mov lr, pc
+ ldr pc, .LdvmFastMethodTraceEnter
ldmfd sp!, {r0-r3}
#endif
- LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+ mov lr, pc
+ ldr pc, [r2, #offMethod_nativeFunc]
#if defined(WITH_INLINE_PROFILING)
ldmfd sp!, {r0-r1}
- LDR_PC_LR ".LdvmFastNativeMethodTraceExit"
+ mov lr, pc
+ ldr pc, .LdvmFastNativeMethodTraceExit
#endif
@ Refresh Jit's on/off status
ldr r3, [rGLUE, #offGlue_ppJitProfTable]
.word dvmMterpCommonExceptionThrown
.LdvmLockObject:
.word dvmLockObject
+.LdvmJitTraceProfilingOff:
+ .word dvmJitTraceProfilingOff
#if defined(WITH_JIT_TUNING)
.LdvmICHitCount:
.word gDvmICHitCount
* ===========================================================================
*/
-/*
- * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
- * Jump to subroutine.
- *
- * May modify IP and LR.
- */
-.macro LDR_PC_LR source
- mov lr, pc
- ldr pc, \source
-.endm
-
.global dvmCompilerTemplateStart
.type dvmCompilerTemplateStart, %function
stmfd sp!, {r0-r2,lr} @ preserve live registers
mov r0, r6
@ r0=rGlue
- LDR_PC_LR ".LdvmFastJavaMethodTraceExit"
+ mov lr, pc
+ ldr pc, .LdvmFastJavaMethodTraceExit
ldmfd sp!, {r0-r2,lr} @ restore live registers
#endif
SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old)
stmfd sp!, {r0-r3} @ preserve r0-r3
mov r1, r6
@ r0=methodToCall, r1=rGlue
- LDR_PC_LR ".LdvmFastMethodTraceEnter"
+ mov lr, pc
+ ldr pc, .LdvmFastMethodTraceEnter
ldmfd sp!, {r0-r3} @ restore r0-r3
#endif
stmfd sp!, {r0-r2,lr} @ preserve clobbered live registers
mov r1, r6
@ r0=methodToCall, r1=rGlue
- LDR_PC_LR ".LdvmFastMethodTraceEnter"
+ mov lr, pc
+ ldr pc, .LdvmFastMethodTraceEnter
ldmfd sp!, {r0-r2,lr} @ restore registers
#endif
mov r0, r2
mov r1, r6
@ r0=JNIMethod, r1=rGlue
- LDR_PC_LR ".LdvmFastMethodTraceEnter"
+ mov lr, pc
+ ldr pc, .LdvmFastMethodTraceEnter
ldmfd sp!, {r0-r3} @ restore r0-r3
#endif
#if defined(WITH_INLINE_PROFILING)
ldmfd sp!, {r0-r1} @ restore r2 and r6
@ r0=JNIMethod, r1=rGlue
- LDR_PC_LR ".LdvmFastNativeMethodTraceExit"
+ mov lr, pc
+ ldr pc, .LdvmFastNativeMethodTraceExit
#endif
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
#endif
ldr pc, .LdvmJitToInterpNoChain
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_PERIODIC_PROFILING
+dvmCompiler_TEMPLATE_PERIODIC_PROFILING:
+/* File: armv5te/TEMPLATE_PERIODIC_PROFILING.S */
+ /*
+ * Increment profile counter for this trace, and decrement
+ * sample counter. If sample counter goes below zero, turn
+ * off profiling.
+ *
+ * On entry
+ * (lr-11) is address of pointer to counter. Note: the counter
+ * actually exists 10 bytes before the return target, but because
+ * we are arriving from thumb mode, lr will have its low bit set.
+ */
+ ldr r0, [lr,#-11]
+ ldr r1, [rGLUE, #offGlue_pProfileCountdown]
+ ldr r2, [r0] @ get counter
+ ldr r3, [r1] @ get countdown timer
+ add r2, #1
+ subs r2, #1
+ blt .LTEMPLATE_PERIODIC_PROFILING_disable_profiling
+ str r2, [r0]
+ str r3, [r1]
+ bx lr
+
+.LTEMPLATE_PERIODIC_PROFILING_disable_profiling:
+ mov r4, lr @ preserve lr
+ ldr r0, .LdvmJitTraceProfilingOff
+ blx r0
+ bx r4
+
.size dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
/* File: armv5te/footer.S */
/*
stmfd sp!, {r0-r3}
mov r0, r2
mov r1, r6
- LDR_PC_LR ".LdvmFastMethodTraceEnter"
+ mov lr, pc
+ ldr pc, .LdvmFastMethodTraceEnter
ldmfd sp!, {r0-r3}
#endif
- LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+ mov lr, pc
+ ldr pc, [r2, #offMethod_nativeFunc]
#if defined(WITH_INLINE_PROFILING)
ldmfd sp!, {r0-r1}
- LDR_PC_LR ".LdvmFastNativeMethodTraceExit"
+ mov lr, pc
+ ldr pc, .LdvmFastNativeMethodTraceExit
#endif
@ Refresh Jit's on/off status
ldr r3, [rGLUE, #offGlue_ppJitProfTable]
.word dvmMterpCommonExceptionThrown
.LdvmLockObject:
.word dvmLockObject
+.LdvmJitTraceProfilingOff:
+ .word dvmJitTraceProfilingOff
#if defined(WITH_JIT_TUNING)
.LdvmICHitCount:
.word gDvmICHitCount
* ===========================================================================
*/
-/*
- * Macro for "MOV LR,PC / LDR PC,xxx", which is not allowed pre-ARMv5.
- * Jump to subroutine.
- *
- * May modify IP and LR.
- */
-.macro LDR_PC_LR source
- mov lr, pc
- ldr pc, \source
-.endm
-
.global dvmCompilerTemplateStart
.type dvmCompilerTemplateStart, %function
stmfd sp!, {r0-r2,lr} @ preserve live registers
mov r0, r6
@ r0=rGlue
- LDR_PC_LR ".LdvmFastJavaMethodTraceExit"
+ mov lr, pc
+ ldr pc, .LdvmFastJavaMethodTraceExit
ldmfd sp!, {r0-r2,lr} @ restore live registers
#endif
SAVEAREA_FROM_FP(r0, rFP) @ r0<- saveArea (old)
stmfd sp!, {r0-r3} @ preserve r0-r3
mov r1, r6
@ r0=methodToCall, r1=rGlue
- LDR_PC_LR ".LdvmFastMethodTraceEnter"
+ mov lr, pc
+ ldr pc, .LdvmFastMethodTraceEnter
ldmfd sp!, {r0-r3} @ restore r0-r3
#endif
stmfd sp!, {r0-r2,lr} @ preserve clobbered live registers
mov r1, r6
@ r0=methodToCall, r1=rGlue
- LDR_PC_LR ".LdvmFastMethodTraceEnter"
+ mov lr, pc
+ ldr pc, .LdvmFastMethodTraceEnter
ldmfd sp!, {r0-r2,lr} @ restore registers
#endif
mov r0, r2
mov r1, r6
@ r0=JNIMethod, r1=rGlue
- LDR_PC_LR ".LdvmFastMethodTraceEnter"
+ mov lr, pc
+ ldr pc, .LdvmFastMethodTraceEnter
ldmfd sp!, {r0-r3} @ restore r0-r3
#endif
#if defined(WITH_INLINE_PROFILING)
ldmfd sp!, {r0-r1} @ restore r2 and r6
@ r0=JNIMethod, r1=rGlue
- LDR_PC_LR ".LdvmFastNativeMethodTraceExit"
+ mov lr, pc
+ ldr pc, .LdvmFastNativeMethodTraceExit
#endif
@ native return; r9=self, r10=newSaveArea
@ equivalent to dvmPopJniLocals
#endif
ldr pc, .LdvmJitToInterpNoChain
+/* ------------------------------ */
+ .balign 4
+ .global dvmCompiler_TEMPLATE_PERIODIC_PROFILING
+dvmCompiler_TEMPLATE_PERIODIC_PROFILING:
+/* File: armv5te/TEMPLATE_PERIODIC_PROFILING.S */
+ /*
+ * Increment profile counter for this trace, and decrement
+ * sample counter. If sample counter goes below zero, turn
+ * off profiling.
+ *
+ * On entry
+ * (lr-11) is address of pointer to counter. Note: the counter
+ * actually exists 10 bytes before the return target, but because
+ * we are arriving from thumb mode, lr will have its low bit set.
+ */
+ ldr r0, [lr,#-11]
+ ldr r1, [rGLUE, #offGlue_pProfileCountdown]
+ ldr r2, [r0] @ get counter
+ ldr r3, [r1] @ get countdown timer
+ add r2, #1
+ subs r2, #1
+ blt .LTEMPLATE_PERIODIC_PROFILING_disable_profiling
+ str r2, [r0]
+ str r3, [r1]
+ bx lr
+
+.LTEMPLATE_PERIODIC_PROFILING_disable_profiling:
+ mov r4, lr @ preserve lr
+ ldr r0, .LdvmJitTraceProfilingOff
+ blx r0
+ bx r4
+
.size dvmCompilerTemplateStart, .-dvmCompilerTemplateStart
/* File: armv5te/footer.S */
/*
stmfd sp!, {r0-r3}
mov r0, r2
mov r1, r6
- LDR_PC_LR ".LdvmFastMethodTraceEnter"
+ mov lr, pc
+ ldr pc, .LdvmFastMethodTraceEnter
ldmfd sp!, {r0-r3}
#endif
- LDR_PC_LR "[r2, #offMethod_nativeFunc]"
+ mov lr, pc
+ ldr pc, [r2, #offMethod_nativeFunc]
#if defined(WITH_INLINE_PROFILING)
ldmfd sp!, {r0-r1}
- LDR_PC_LR ".LdvmFastNativeMethodTraceExit"
+ mov lr, pc
+ ldr pc, .LdvmFastNativeMethodTraceExit
#endif
@ Refresh Jit's on/off status
ldr r3, [rGLUE, #offGlue_ppJitProfTable]
.word dvmMterpCommonExceptionThrown
.LdvmLockObject:
.word dvmLockObject
+.LdvmJitTraceProfilingOff:
+ .word dvmJitTraceProfilingOff
#if defined(WITH_JIT_TUNING)
.LdvmICHitCount:
.word gDvmICHitCount
#if defined(WITH_JIT)
+/* Subset of defines from mterp/x86/header.S */
+#define rGLUE (%ebp)
+#define rPC %esi
+#define rFP %edi
+#define rINST %ebx
+
/*
* This is a #include, not a %include, because we want the C pre-processor
* to expand the macros into assembler assignment statements.
dvmCompiler_TEMPLATE_INTERPRET:
/* File: ia32/TEMPLATE_INTERPRET.S */
/*
- * TODO: figure out how best to do this on x86, as we don't have
- * an lr equivalent and probably don't want to push.
+ * This handler is a bit odd - it may be called via chaining or
+ * from static code and is expected to cause control to flow
+ * to the interpreter. The problem is where to find the Dalvik
+ * PC of the next instruction. When called via chaining, the dPC
+ * will be located at *rp. When called from static code, rPC is
+ * valid and rp is a real return pointer (that should be ignored).
+ * The Arm target deals with this by using the link register as
+ * a flag. If it is zero, we know we were called from static code.
+ * If non-zero, it points to the chain cell containing dPC.
+ * For x86, we'll infer the source by looking where rp points.
+ * If it points to anywhere within the code cache, we'll assume
+ * we got here via chaining. Otherwise, we'll assume rPC is valid.
*
- * This handler transfers control to the interpeter without performing
- * any lookups. It may be called either as part of a normal chaining
- * operation, or from the transition code in header.S. We distinquish
- * the two cases by looking at the link register. If called from a
- * translation chain, it will point to the chaining Dalvik PC -3.
* On entry:
- * lr - if NULL:
- * r1 - the Dalvik PC to begin interpretation.
- * else
- * [lr, #3] contains Dalvik PC to begin interpretation
- * rGLUE - pointer to interpState
- * rFP - Dalvik frame pointer
- *
- *cmp lr, #0
- *ldrne r1,[lr, #3]
- *ldr r2, .LinterpPunt
- *mov r0, r1 @ set Dalvik PC
- *bx r2
- *@ doesn't return
+ * (TOS)<- return pointer or pointer to dPC
*/
+ movl rGLUE,%ecx
+ movl $.LinterpPunt,%edx
+ pop %eax
+ cmpl %eax,offGlue_jitCacheEnd(%ecx)
+ ja 1f
+ cmpl %eax,offGlue_jitCacheStart(%ecx)
+ jb 1f
+ movl %eax,rPC
+1:
+ jmp *(%edx)
.LinterpPunt:
.long dvmJitToInterpPunt
.text
.align 4
-/*
- * FIXME - need a cacheflush for x86
- */
- .global cacheflush
-cacheflush:
- movl $0xdeadf0f0, %eax
- call *%eax
-
.global dmvCompilerTemplateEnd
dmvCompilerTemplateEnd:
hprofStartup_Stack();
#endif
- hprof_context_t *ctx = malloc(sizeof(*ctx));
+ hprof_context_t *ctx = (hprof_context_t *)malloc(sizeof(*ctx));
if (ctx == NULL) {
LOGE("hprof: can't allocate context.\n");
return NULL;
* Create a new context struct for the start of the file. We
* heap-allocate it so we can share the "free" function.
*/
- hprof_context_t *headCtx = malloc(sizeof(*headCtx));
+ hprof_context_t *headCtx = (hprof_context_t *)malloc(sizeof(*headCtx));
if (headCtx == NULL) {
LOGE("hprof: can't allocate context.\n");
hprofFreeContext(tailCtx);
if (obj == NULL) {
return;
}
- ctx = arg;
+ ctx = (hprof_context_t *)arg;
ctx->gcScanState = xlate[type];
ctx->gcThreadSerialNumber = threadId;
hprofMarkRootObject(ctx, obj, 0);
assert(ptr != NULL);
assert(arg != NULL);
- obj = ptr;
- ctx = arg;
+ obj = (Object *)ptr;
+ ctx = (hprof_context_t *)arg;
hprofDumpHeapObject(ctx, obj);
}
ctx->fd = fd;
ctx->curRec.allocLen = 128;
- ctx->curRec.body = malloc(ctx->curRec.allocLen);
+ ctx->curRec.body = (unsigned char *)malloc(ctx->curRec.allocLen);
//xxx check for/return an error
if (writeHeader) {
if (newAllocLen < minSize) {
newAllocLen = rec->allocLen + nmore + nmore/2;
}
- newBody = realloc(rec->body, newAllocLen);
+ newBody = (unsigned char *)realloc(rec->body, newAllocLen);
if (newBody != NULL) {
rec->body = newBody;
rec->allocLen = newAllocLen;
LOGV("+++ increasing breakpoint set size to %d\n", newSize);
/* pSet->breakpoints will be NULL on first entry */
- newVec = realloc(pSet->breakpoints, newSize * sizeof(Breakpoint));
+ newVec = (Breakpoint*)realloc(pSet->breakpoints, newSize * sizeof(Breakpoint));
if (newVec == NULL)
return false;
saveArea = SAVEAREA_FROM_FP(fp);
method = saveArea->method;
- if (!dvmIsBreakFrame(fp) && !dvmIsNativeMethod(method))
+ if (!dvmIsBreakFrame((u4*)fp) && !dvmIsNativeMethod(method))
break;
prevFp = fp;
}
*/
LOGV("##### init step while in native method\n");
fp = prevFp;
- assert(!dvmIsBreakFrame(fp));
+ assert(!dvmIsBreakFrame((u4*)fp));
assert(dvmIsNativeMethod(SAVEAREA_FROM_FP(fp)->method));
saveArea = SAVEAREA_FROM_FP(fp);
}
#if defined(WITH_JIT)
/*
- * There are six entry points from the compiled code to the interpreter:
+ * There are seven entry points from the compiled code to the interpreter:
* 1) dvmJitToInterpNormal: find if there is a corresponding compilation for
* the new dalvik PC. If so, chain the originating compilation with the
* target then jump to it.
*/
unsigned char** ppJitProfTable; // Used to refresh pJitProfTable
int icRechainCount; // Count down to next rechain request
-#endif
+ const void* pProfileCountdown; // Address of profile countdown timer
- bool debugIsMethodEntry; // used for method entry event triggers
-#if defined(WITH_TRACKREF_CHECKS)
- int debugTrackedRefStart; // tracked refs from prior invocations
-#endif
-
-#if defined(WITH_JIT)
struct JitToInterpEntries jitToInterpEntries;
+ const void* jitCacheStart; // Code cache boundaries
+ const void* jitCacheEnd;
+
int currTraceRun;
int totalTraceLen; // Number of Dalvik insts in trace
const u2* currTraceHead; // Start of the trace we're building
double calleeSave[JIT_CALLEE_SAVE_DOUBLE_COUNT];
#endif
+ bool debugIsMethodEntry; // used for method entry event triggers
+#if defined(WITH_TRACKREF_CHECKS)
+ int debugTrackedRefStart; // tracked refs from prior invocations
+#endif
+
+
} InterpState;
/*
// Create a copy of the InterpState
memcpy(&(shadowSpace->interpState), interpState, sizeof(InterpState));
- shadowSpace->interpState.fp = shadowSpace->shadowFP;
+ shadowSpace->interpState.fp = (u4*)shadowSpace->shadowFP;
shadowSpace->interpState.interpStackEnd = (u1*)shadowSpace->registerSpace;
// Create a copy of the stack
Thread *self = dvmThreadSelf();
ShadowSpace *shadowSpace = self->shadowSpace;
// Official InterpState structure
- InterpState *realGlue = shadowSpace->glue;
+ InterpState *realGlue = (InterpState*)shadowSpace->glue;
shadowSpace->endPC = pc;
shadowSpace->endShadowFP = fp;
shadowSpace->jitExitState = exitState;
LOGD("JIT: %d Translation chains, %d interp stubs",
gDvmJit.translationChains, stubs);
- if (gDvmJit.profile) {
+ if (gDvmJit.profileMode == kTraceProfilingContinuous) {
dvmCompilerSortAndPrintTraceProfiles();
}
}
const u2* npc = gDvmJit.pJitEntryTable[idx].dPC;
if (npc != NULL) {
bool hideTranslation = dvmJitHideTranslation();
-
if (npc == dPC) {
+ int offset = (gDvmJit.profileMode >= kTraceProfilingContinuous) ?
+ 0 : gDvmJit.pJitEntryTable[idx].u.info.profileOffset;
+ intptr_t codeAddress =
+ (intptr_t)gDvmJit.pJitEntryTable[idx].codeAddress;
#if defined(WITH_JIT_TUNING)
gDvmJit.addrLookupsFound++;
#endif
- return hideTranslation ?
- NULL : gDvmJit.pJitEntryTable[idx].codeAddress;
+ return hideTranslation ? NULL : (void *)(codeAddress + offset);
} else {
int chainEndMarker = gDvmJit.jitTableSize;
while (gDvmJit.pJitEntryTable[idx].u.info.chain != chainEndMarker) {
idx = gDvmJit.pJitEntryTable[idx].u.info.chain;
if (gDvmJit.pJitEntryTable[idx].dPC == dPC) {
+ int offset = (gDvmJit.profileMode >=
+ kTraceProfilingContinuous) ? 0 :
+ gDvmJit.pJitEntryTable[idx].u.info.profileOffset;
+ intptr_t codeAddress =
+ (intptr_t)gDvmJit.pJitEntryTable[idx].codeAddress;
#if defined(WITH_JIT_TUNING)
gDvmJit.addrLookupsFound++;
#endif
- return hideTranslation ?
- NULL : gDvmJit.pJitEntryTable[idx].codeAddress;
+ return hideTranslation ? NULL :
+ (void *)(codeAddress + offset);
}
}
}
* NOTE: Once a codeAddress field transitions from initial state to
* JIT'd code, it must not be altered without first halting all
* threads. This routine should only be called by the compiler
- * thread.
+ * thread. We defer the setting of the profile prefix size until
+ * after the new code address is set to ensure that the prefix offset
+ * is never applied to the initial interpret-only translation. All
+ * translations with non-zero profile prefixes will still be correct
+ * if entered as if the profile offset is 0, but the interpret-only
+ * template cannot handle a non-zero prefix.
*/
-void dvmJitSetCodeAddr(const u2* dPC, void *nPC, JitInstructionSetType set) {
+void dvmJitSetCodeAddr(const u2* dPC, void *nPC, JitInstructionSetType set,
+ int profilePrefixSize)
+{
JitEntryInfoUnion oldValue;
JitEntryInfoUnion newValue;
JitEntry *jitEntry = lookupAndAdd(dPC, false);
oldValue.infoWord, newValue.infoWord,
&jitEntry->u.infoWord) != 0);
jitEntry->codeAddress = nPC;
+ newValue.info.profileOffset = profilePrefixSize;
+ jitEntry->u = newValue;
}
/*
p->u.info.chain = chain;
}
}
+
dvmUnlockMutex(&gDvmJit.tableLock);
free(pOldTable);
unsigned int i;
dvmLockMutex(&gDvmJit.tableLock);
+
+ /* Note: If need to preserve any existing counts. Do so here. */
+ for (i=0; i < JIT_PROF_BLOCK_BUCKETS; i++) {
+ if (gDvmJit.pJitTraceProfCounters->buckets[i])
+ memset((void *) gDvmJit.pJitTraceProfCounters->buckets[i],
+ 0, sizeof(JitTraceCounter_t) * JIT_PROF_BLOCK_ENTRIES);
+ }
+ gDvmJit.pJitTraceProfCounters->next = 0;
+
memset((void *) jitEntry, 0, sizeof(JitEntry) * size);
for (i=0; i< size; i++) {
jitEntry[i].u.info.chain = size; /* Initialize chain termination */
}
/*
+ * Return the address of the next trace profile counter. This address
+ * will be embedded in the generated code for the trace, and thus cannot
+ * change while the trace exists.
+ */
+JitTraceCounter_t *dvmJitNextTraceCounter()
+{
+ int idx = gDvmJit.pJitTraceProfCounters->next / JIT_PROF_BLOCK_ENTRIES;
+ int elem = gDvmJit.pJitTraceProfCounters->next % JIT_PROF_BLOCK_ENTRIES;
+ JitTraceCounter_t *res;
+ /* Lazily allocate blocks of counters */
+ if (!gDvmJit.pJitTraceProfCounters->buckets[idx]) {
+ JitTraceCounter_t *p =
+ (JitTraceCounter_t*) calloc(JIT_PROF_BLOCK_ENTRIES, sizeof(*p));
+ if (!p) {
+ LOGE("Failed to allocate block of trace profile counters");
+ dvmAbort();
+ }
+ gDvmJit.pJitTraceProfCounters->buckets[idx] = p;
+ }
+ res = &gDvmJit.pJitTraceProfCounters->buckets[idx][elem];
+ gDvmJit.pJitTraceProfCounters->next++;
+ return res;
+}
+
+/*
* Float/double conversion requires clamping to min and max of integer form. If
* target doesn't support this normally, use these.
*/
return (s8)f;
}
+/* Should only be called by the compiler thread */
+void dvmJitChangeProfileMode(TraceProfilingModes newState)
+{
+ if (gDvmJit.profileMode != newState) {
+ gDvmJit.profileMode = newState;
+ dvmJitUnchainAll();
+ }
+}
+
+void dvmJitTraceProfilingOn()
+{
+ if (gDvmJit.profileMode == kTraceProfilingPeriodicOff)
+ dvmCompilerWorkEnqueue(NULL, kWorkOrderProfileMode,
+ (void*) kTraceProfilingPeriodicOn);
+ else if (gDvmJit.profileMode == kTraceProfilingDisabled)
+ dvmCompilerWorkEnqueue(NULL, kWorkOrderProfileMode,
+ (void*) kTraceProfilingContinuous);
+}
+
+void dvmJitTraceProfilingOff()
+{
+ if (gDvmJit.profileMode == kTraceProfilingPeriodicOn)
+ dvmCompilerWorkEnqueue(NULL, kWorkOrderProfileMode,
+ (void*) kTraceProfilingPeriodicOff);
+ else if (gDvmJit.profileMode == kTraceProfilingContinuous)
+ dvmCompilerWorkEnqueue(NULL, kWorkOrderProfileMode,
+ (void*) kTraceProfilingDisabled);
+}
+
#endif /* WITH_JIT */
}
/*
+ * The width of the chain field in JitEntryInfo sets the upper
+ * bound on the number of translations. Be careful if changing
+ * the size of JitEntry struct - the Dalvik PC to JitEntry
+ * hash functions have built-in knowledge of the size.
+ */
+#define JIT_ENTRY_CHAIN_WIDTH 2
+#define JIT_MAX_ENTRIES (1 << (JIT_ENTRY_CHAIN_WIDTH * 8))
+
+/*
+ * The trace profiling counters are allocated in blocks and individual
+ * counters must not move so long as any referencing trace exists.
+ */
+#define JIT_PROF_BLOCK_ENTRIES 1024
+#define JIT_PROF_BLOCK_BUCKETS (JIT_MAX_ENTRIES / JIT_PROF_BLOCK_ENTRIES)
+
+typedef s4 JitTraceCounter_t;
+
+typedef struct JitTraceProfCounters {
+ unsigned int next;
+ JitTraceCounter_t *buckets[JIT_PROF_BLOCK_BUCKETS];
+} JitTraceProfCounters;
+
+/*
* Entries in the JIT's address lookup hash table.
* Fields which may be updated by multiple threads packed into a
* single 32-bit word to allow use of atomic update.
unsigned int inlineCandidate:1;
unsigned int profileEnabled:1;
JitInstructionSetType instructionSet:4;
- unsigned int unused:8;
+ unsigned int profileOffset:8;
u2 chain; /* Index of next in chain */
} JitEntryInfo;
struct JitEntry *dvmFindJitEntry(const u2* pc);
s8 dvmJitd2l(double d);
s8 dvmJitf2l(float f);
-void dvmJitSetCodeAddr(const u2* dPC, void *nPC, JitInstructionSetType set);
+void dvmJitSetCodeAddr(const u2* dPC, void *nPC, JitInstructionSetType set,
+ int profilePrefixSize);
void dvmJitAbortTraceSelect(InterpState* interpState);
+JitTraceCounter_t *dvmJitNextTraceCounter(void);
+void dvmJitTraceProfilingOff(void);
+void dvmJitTraceProfilingOn(void);
+void dvmJitChangeProfileMode(TraceProfilingModes newState);
#endif /*_DALVIK_INTERP_JIT*/
memset(stackPtr - (method->outsSize*4), 0xaf, stackReq);
#endif
#ifdef EASY_GDB
- breakSaveBlock->prevSave = FP_FROM_SAVEAREA(self->curFrame);
+ breakSaveBlock->prevSave = (StackSaveArea*)FP_FROM_SAVEAREA(self->curFrame);
saveBlock->prevSave = breakSaveBlock;
#endif
#ifdef EASY_GDB
if (self->curFrame == NULL)
breakSaveBlock->prevSave = NULL;
- else
- breakSaveBlock->prevSave = FP_FROM_SAVEAREA(self->curFrame);
+ else {
+ void* fp = FP_FROM_SAVEAREA(self->curFrame);
+ breakSaveBlock->prevSave = (StackSaveArea*)fp;
+ }
saveBlock->prevSave = breakSaveBlock;
#endif
memset(stackPtr, 0xaf, stackReq);
#endif
#ifdef EASY_GDB
- saveBlock->prevSave = FP_FROM_SAVEAREA(self->curFrame);
+ saveBlock->prevSave = (StackSaveArea*)FP_FROM_SAVEAREA(self->curFrame);
#endif
saveBlock->prevFrame = self->curFrame;
{
StackSaveArea* saveBlock = SAVEAREA_FROM_FP(self->curFrame);
- assert(!dvmIsBreakFrame(self->curFrame));
+ assert(!dvmIsBreakFrame((u4*)self->curFrame));
if (saveBlock->method != SAVEAREA_FROM_FP(saveBlock->prevFrame)->method) {
/*
* The previous frame doesn't have the same method pointer -- we've
* been asked to pop too much.
*/
- assert(dvmIsBreakFrame(saveBlock->prevFrame) ||
+ assert(dvmIsBreakFrame((u4*)saveBlock->prevFrame) ||
!dvmIsNativeMethod(
SAVEAREA_FROM_FP(saveBlock->prevFrame)->method));
return false;
return false;
saveBlock = SAVEAREA_FROM_FP(self->curFrame);
- assert(!dvmIsBreakFrame(self->curFrame));
+ assert(!dvmIsBreakFrame((u4*)self->curFrame));
/*
* Remove everything up to the break frame. If this was a call into
* Because we leave no space for local variables, "curFrame" points
* directly at the method arguments.
*/
- (*method->nativeFunc)(self->curFrame, pResult, method, self);
+ (*method->nativeFunc)((u4*)self->curFrame, pResult, method, self);
TRACE_METHOD_EXIT(self, method);
} else {
dvmInterpret(self, method, pResult);
* Because we leave no space for local variables, "curFrame" points
* directly at the method arguments.
*/
- (*method->nativeFunc)(self->curFrame, pResult, method, self);
+ (*method->nativeFunc)((u4*)self->curFrame, pResult, method, self);
TRACE_METHOD_EXIT(self, method);
} else {
dvmInterpret(self, method, pResult);
* Because we leave no space for local variables, "curFrame" points
* directly at the method arguments.
*/
- (*method->nativeFunc)(self->curFrame, &retval, method, self);
+ (*method->nativeFunc)((u4*)self->curFrame, &retval, method, self);
TRACE_METHOD_EXIT(self, method);
} else {
dvmInterpret(self, method, &retval);
int count = 0;
for ( ; fp != NULL; fp = SAVEAREA_FROM_FP(fp)->prevFrame) {
- if (!dvmIsBreakFrame(fp))
+ if (!dvmIsBreakFrame((u4*)fp))
count++;
}
StackSaveArea* saveArea;
retry:
- if (dvmIsBreakFrame(caller)) {
+ if (dvmIsBreakFrame((u4*)caller)) {
/* pop up one more */
caller = SAVEAREA_FROM_FP(caller)->prevFrame;
if (caller == NULL)
void* callerCaller;
/* at the top? */
- if (dvmIsBreakFrame(caller) && SAVEAREA_FROM_FP(caller)->prevFrame == NULL)
+ if (dvmIsBreakFrame((u4*)caller) && SAVEAREA_FROM_FP(caller)->prevFrame == NULL)
return NULL;
/* go one more */
int i;
/* at the top? */
- if (dvmIsBreakFrame(caller) && SAVEAREA_FROM_FP(caller)->prevFrame == NULL)
+ if (dvmIsBreakFrame((u4*)caller) && SAVEAREA_FROM_FP(caller)->prevFrame == NULL)
return NULL;
/* Walk up two frames if possible. */
return false;
for (idx = 0; fp != NULL; fp = SAVEAREA_FROM_FP(fp)->prevFrame) {
- if (!dvmIsBreakFrame(fp))
+ if (!dvmIsBreakFrame((u4*)fp))
array[idx++] = SAVEAREA_FROM_FP(fp)->method;
}
assert(idx == depth);
{
void* framePtr = thread->curFrame;
- if (framePtr == NULL || dvmIsBreakFrame(framePtr))
+ if (framePtr == NULL || dvmIsBreakFrame((u4*)framePtr))
return false;
const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
* The "currentPc" is updated whenever we execute an instruction that
* might throw an exception. Show it here.
*/
- if (framePtr != NULL && !dvmIsBreakFrame(framePtr)) {
+ if (framePtr != NULL && !dvmIsBreakFrame((u4*)framePtr)) {
saveArea = SAVEAREA_FROM_FP(framePtr);
if (saveArea->xtra.currentPc != NULL)
saveArea = SAVEAREA_FROM_FP(framePtr);
method = saveArea->method;
- if (dvmIsBreakFrame(framePtr)) {
+ if (dvmIsBreakFrame((u4*)framePtr)) {
//dvmPrintDebugMessage(target, " (break frame)\n");
} else {
int relPc;
while (pBuf->curLen + newCount > pBuf->maxLen)
pBuf->maxLen *= 2;
- newPtr = realloc(pBuf->storage, pBuf->maxLen);
+ newPtr = (u1*) realloc(pBuf->storage, pBuf->maxLen);
if (newPtr == NULL) {
LOGE("realloc(%d) failed\n", pBuf->maxLen);
abort();
static JdwpNetState*
adbStateAlloc(void)
{
- JdwpNetState* netState = calloc(sizeof(*netState),1);
+ JdwpNetState* netState = (JdwpNetState*) calloc(sizeof(*netState),1);
netState->controlSock = -1;
netState->clientSock = -1;
glue->pJitProfTable = gDvmJit.pProfTable;
glue->ppJitProfTable = &gDvmJit.pProfTable;
glue->jitThreshold = gDvmJit.threshold;
+ glue->jitCacheStart = gDvmJit.codeCache;
+ glue->jitCacheEnd = (char*)gDvmJit.codeCache + gDvmJit.codeCacheSize;
+ glue->pProfileCountdown = &gDvmJit.profileCountdown;
#endif
#if defined(WITH_INLINE_PROFILING)
/*
#endif
/* back up to previous frame and see if we hit a break */
- fp = saveArea->prevFrame;
+ fp = (u4*)saveArea->prevFrame;
assert(fp != NULL);
if (dvmIsBreakFrame(fp)) {
/* bail without popping the method frame from stack */
* the "catch" blocks.
*/
catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
- exception, false, (void*)&fp);
+ exception, false, (void**)(void*)&fp);
/*
* Restore the stack bounds after an overflow. This isn't going to
curMethod = methodToCall;
methodClassDex = curMethod->clazz->pDvmDex;
pc = methodToCall->insns;
- fp = self->curFrame = newFp;
+ self->curFrame = fp = newFp;
#ifdef EASY_GDB
debugSaveArea = SAVEAREA_FROM_FP(newFp);
#endif
MTERP_OFFSET(offGlue_jitThreshold, MterpGlue, jitThreshold, 76)
MTERP_OFFSET(offGlue_ppJitProfTable, MterpGlue, ppJitProfTable, 80)
MTERP_OFFSET(offGlue_icRechainCount, MterpGlue, icRechainCount, 84)
+MTERP_OFFSET(offGlue_pProfileCountdown, MterpGlue, pProfileCountdown, 88)
+#if defined(WITH_SELF_VERIFICATION)
+MTERP_OFFSET(offGlue_jitCacheStart, MterpGlue, jitCacheStart, 124)
+MTERP_OFFSET(offGlue_jitCacheEnd, MterpGlue, jitCacheEnd, 128)
+#else
+MTERP_OFFSET(offGlue_jitCacheStart, MterpGlue, jitCacheStart, 120)
+MTERP_OFFSET(offGlue_jitCacheEnd, MterpGlue, jitCacheEnd, 124)
+#endif
#endif
/* make sure all JValue union members are stored at the same offset */
MTERP_OFFSET(offGlue_retval_z, MterpGlue, retval.z, 8)
.LintMax:
.long 0x7FFFFFFF
+
.global dvmAsmInstructionStart
.type dvmAsmInstructionStart, %function
dvmAsmInstructionStart = .L_OP_NOP
Mterp notes:
Some key interpreter variables will be assigned to registers. Note that each
-will also have an associated spill location (mostly used useful for those assigned
+will also have an associated spill location (mostly useful for those assigned
to callee save registers).
nick reg purpose
o High order 16 bits of ebx must be zero on entry to handler
o rPC, rFP, rINSTw/rINSTbl valid on handler entry and exit
o eax, edx and ecx are scratch, rINSTw/ebx sometimes scratch
- o rPC is in the caller save set, and will be killed across external calls. Don't
- forget to SPILL/UNSPILL it around call points
*/
#define SPILL_TMP3(reg) movl reg,TMP_SPILL3(%ebp)
#define UNSPILL_TMP3(reg) movl TMP_SPILL3(%ebp),reg
+#if defined(WITH_JIT)
+.macro GET_JIT_PROF_TABLE _glue _reg
+ movl offGlue_pJitProfTable(\_glue),\_reg
+.endm
+.macro GET_JIT_THRESHOLD _glue _reg
+ movl offGlue_jitThreshold(\_glue),\_reg
+.endm
+#endif
+
/* save/restore the PC and/or FP from the glue struct */
.macro SAVE_PC_FP_TO_GLUE _reg
movl rGLUE,\_reg
*/
#include "../common/asm-constants.h"
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
+
.global dvmAsmInstructionStart
.type dvmAsmInstructionStart, %function
#if defined(WITH_JIT)
/*
- * Placeholder entries for x86 JIT
+ * JIT-related re-entries into the interpreter. In general, if the
+ * exit from a translation can at some point be chained, the entry
+ * here requires that control arrived via a call, and that the "rp"
+ * on TOS is actually a pointer to a 32-bit cell containing the Dalvik PC
+ * of the next insn to handle. If no chaining will happen, the entry
+ * should be reached via a direct jump and rPC set beforehand.
*/
+
.global dvmJitToInterpPunt
+/*
+ * The compiler will generate a jump to this entry point when it is
+ * having difficulty translating a Dalvik instruction. We must skip
+ * the code cache lookup & prevent chaining to avoid bouncing between
+ * the interpreter and code cache. rPC must be set on entry.
+ */
dvmJitToInterpPunt:
+#if defined(WITH_JIT_TUNING)
+ movl rPC, OUT_ARG0(%esp)
+ call dvmBumpPunt
+#endif
+ FETCH_INST_R %edx
+ GOTO_NEXT_R %edx
+
.global dvmJitToInterpSingleStep
+/*
+ * Return to the interpreter to handle a single instruction.
+ * Should be reached via a call.
+ * On entry:
+ * 0(%esp) <= native return address within trace
+ * rPC <= Dalvik PC of this instruction
+ * OUT_ARG0+4(%esp) <= Dalvik PC of next instruction
+ */
dvmJitToInterpSingleStep:
+ pop %eax
+ movl rGLUE, %ecx
+ movl OUT_ARG0(%esp), %edx
+ movl %eax,offGlue_jitResumeNPC(%ecx)
+ movl %edx,offGlue_jitResumeDPC(%ecx)
+ movl $kInterpEntryInstr,offGlue_entryPoint(%ecx)
+ movl $1,rINST # changeInterp <= true
+ jmp common_gotoBail
+
.global dvmJitToInterpNoChainNoProfile
+/*
+ * Return from the translation cache to the interpreter to do method
+ * invocation. Check if the translation exists for the callee, but don't
+ * chain to it. rPC must be set on entry.
+ */
dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+ call dvmBumpNoChain
+#endif
+ movl rPC,OUT_ARG0(%esp)
+ call dvmJitGetCodeAddr # is there a translation?
+ movl rGLUE,%ecx
+ movl offGlue_self(%ecx), %ecx # ecx <- glue->self
+ movl %eax,offThread_inJitCodeCache(%ecx) # set inJitCodeCache flag
+ cmpl $0, %eax
+ jz 1f
+ call *%eax # exec translation if we've got one
+ # won't return
+1:
+ FETCH_INST_R %edx
+ GOTO_NEXT_R %edx
+
+/*
+ * Return from the translation cache and immediately request a
+ * translation fro the exit target, but don't attempt to chain.
+ * rPC set on entry.
+ */
.global dvmJitToInterpTraceSelectNoChain
dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+ call dvmBumpNoChain
+#endif
+ movl rPC,OUT_ARG0(%esp)
+ call dvmJitGetCodeAddr # is there a translation?
+ movl rGLUE,%ecx
+ movl offGlue_self(%ecx),%ecx
+ cmpl $0,%eax
+ movl %eax,offThread_inJitCodeCache(%ecx) # set inJitCodeCache flag
+ jz 1f
+ call *%eax # jump to tranlation
+ # won't return
+
+/* No Translation - request one */
+1:
+ GET_JIT_PROF_TABLE %ecx %eax
+ cmpl $0, %eax # JIT enabled?
+ jnz 2f # Request one if so
+ FETCH_INST_R %edx # Continue interpreting if not
+ GOTO_NEXT_R %edx
+2:
+ movl $kJitTSelectRequestHot,rINST # ask for trace select
+ jmp common_selectTrace
+
+/*
+ * Return from the translation cache and immediately request a
+ * translation for the exit target. Reached via a call, and
+ * (TOS)->rPC.
+ */
.global dvmJitToInterpTraceSelect
dvmJitToInterpTraceSelect:
+ pop rINST # save chain cell address in callee save reg
+ movl (rINST),rPC
+ movl rPC,OUT_ARG0(%esp)
+ call dvmJitGetCodeAddr # is there a translation?
+ cmpl $0,%eax
+ jz 1b # no - ask for one
+ movl %eax,OUT_ARG0(%esp)
+# FIXME - need to adjust rINST to beginning of sequence
+ movl rINST,OUT_ARG1(%esp)
+ call dvmJitChain # Attempt dvmJitChain(codeAddr,chainAddr)
+ cmpl $0,%eax # Success?
+ jz toInterpreter # didn't chain - interpret
+ call *%eax
+ # won't return
+
+/*
+ * Placeholder entries for x86 JIT
+ */
.global dvmJitToInterpBackwardBranch
dvmJitToInterpBackwardBranch:
.global dvmJitToInterpNormal
dvmJitToInterpNormal:
.global dvmJitToInterpNoChain
dvmJitToInterpNoChain:
+toInterpreter:
jmp common_abort
#endif
*/
common_backwardBranch:
movl rGLUE,%ecx
- call common_periodicChecks # Note: expects rPC to be preserved
+ call common_periodicChecks # rPC and ecx/rGLUE preserved
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE %ecx %edx
ADVANCE_PC_INDEXED rINST
+ cmpl $0,%edx
FETCH_INST
+ jz 1f # Profiling off - continue
+ .global updateProfile
+updateProfile:
+common_updateProfile:
+ # quick & dirty hash
+ movl rPC, %eax
+ shrl $12, %eax
+ xorl rPC, %eax
+ andl $((1<<JIT_PROF_SIZE_LOG_2)-1),%eax
+ decb (%edx,%eax)
+ jz 2f
+1:
GOTO_NEXT
+2:
+/*
+ * Here, we switch to the debug interpreter to request
+ * trace selection. First, though, check to see if there
+ * is already a native translation in place (and, if so,
+ * jump to it now.
+ */
+ GET_JIT_THRESHOLD %ecx rINST
+ EXPORT_PC
+ movb rINSTbl,(%edx,%eax) # reset counter
+ movl offGlue_self(%ecx),rINST
+ movl rPC,OUT_ARG0(%esp)
+ call dvmJitGetCodeAddr # already have one?
+ movl %eax,offThread_inJitCodeCache(rINST) # set the inJitCodeCache flag
+ cmpl $0,%eax
+ jz 1f
+ call *%eax # FIXME: decide call vs/ jmp!. No return either way
+1:
+ movl $kJitTSelectRequest,%eax
+ # On entry, eax<- jitState, rPC valid
+common_selectTrace:
+ movl rGLUE,%ecx
+ movl %eax,offGlue_jitState(%ecx)
+ movl $kInterpEntryInstr,offGlue_entryPoint(%ecx)
+ movl $1,rINST
+ jmp common_gotoBail
+#else
+ ADVANCE_PC_INDEXED rINST
+ FETCH_INST
+ GOTO_NEXT
+#endif
#endif
/* back up to previous frame and see if we hit a break */
- fp = saveArea->prevFrame;
+ fp = (u4*)saveArea->prevFrame;
assert(fp != NULL);
if (dvmIsBreakFrame(fp)) {
/* bail without popping the method frame from stack */
* the "catch" blocks.
*/
catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
- exception, false, (void*)&fp);
+ exception, false, (void**)(void*)&fp);
/*
* Restore the stack bounds after an overflow. This isn't going to
curMethod = methodToCall;
methodClassDex = curMethod->clazz->pDvmDex;
pc = methodToCall->insns;
- fp = self->curFrame = newFp;
+ self->curFrame = fp = newFp;
#ifdef EASY_GDB
debugSaveArea = SAVEAREA_FROM_FP(newFp);
#endif
#endif
/* back up to previous frame and see if we hit a break */
- fp = saveArea->prevFrame;
+ fp = (u4*)saveArea->prevFrame;
assert(fp != NULL);
if (dvmIsBreakFrame(fp)) {
/* bail without popping the method frame from stack */
* the "catch" blocks.
*/
catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
- exception, false, (void*)&fp);
+ exception, false, (void**)(void*)&fp);
/*
* Restore the stack bounds after an overflow. This isn't going to
curMethod = methodToCall;
methodClassDex = curMethod->clazz->pDvmDex;
pc = methodToCall->insns;
- fp = self->curFrame = newFp;
+ self->curFrame = fp = newFp;
#ifdef EASY_GDB
debugSaveArea = SAVEAREA_FROM_FP(newFp);
#endif
#endif
/* back up to previous frame and see if we hit a break */
- fp = saveArea->prevFrame;
+ fp = (u4*)saveArea->prevFrame;
assert(fp != NULL);
if (dvmIsBreakFrame(fp)) {
/* bail without popping the method frame from stack */
* the "catch" blocks.
*/
catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
- exception, false, (void*)&fp);
+ exception, false, (void**)(void*)&fp);
/*
* Restore the stack bounds after an overflow. This isn't going to
curMethod = methodToCall;
methodClassDex = curMethod->clazz->pDvmDex;
pc = methodToCall->insns;
- fp = self->curFrame = newFp;
+ self->curFrame = fp = newFp;
#ifdef EASY_GDB
debugSaveArea = SAVEAREA_FROM_FP(newFp);
#endif
#endif
/* back up to previous frame and see if we hit a break */
- fp = saveArea->prevFrame;
+ fp = (u4*)saveArea->prevFrame;
assert(fp != NULL);
if (dvmIsBreakFrame(fp)) {
/* bail without popping the method frame from stack */
* the "catch" blocks.
*/
catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
- exception, false, (void*)&fp);
+ exception, false, (void**)(void*)&fp);
/*
* Restore the stack bounds after an overflow. This isn't going to
curMethod = methodToCall;
methodClassDex = curMethod->clazz->pDvmDex;
pc = methodToCall->insns;
- fp = self->curFrame = newFp;
+ self->curFrame = fp = newFp;
#ifdef EASY_GDB
debugSaveArea = SAVEAREA_FROM_FP(newFp);
#endif
#endif
/* back up to previous frame and see if we hit a break */
- fp = saveArea->prevFrame;
+ fp = (u4*)saveArea->prevFrame;
assert(fp != NULL);
if (dvmIsBreakFrame(fp)) {
/* bail without popping the method frame from stack */
* the "catch" blocks.
*/
catchRelPc = dvmFindCatchBlock(self, pc - curMethod->insns,
- exception, false, (void*)&fp);
+ exception, false, (void**)(void*)&fp);
/*
* Restore the stack bounds after an overflow. This isn't going to
curMethod = methodToCall;
methodClassDex = curMethod->clazz->pDvmDex;
pc = methodToCall->insns;
- fp = self->curFrame = newFp;
+ self->curFrame = fp = newFp;
#ifdef EASY_GDB
debugSaveArea = SAVEAREA_FROM_FP(newFp);
#endif
#if defined(WITH_JIT)
/*
- * Placeholder entries for x86 JIT
+ * JIT-related re-entries into the interpreter. In general, if the
+ * exit from a translation can at some point be chained, the entry
+ * here requires that control arrived via a call, and that the "rp"
+ * on TOS is actually a pointer to a 32-bit cell containing the Dalvik PC
+ * of the next insn to handle. If no chaining will happen, the entry
+ * should be reached via a direct jump and rPC set beforehand.
*/
+
.global dvmJitToInterpPunt
+/*
+ * The compiler will generate a jump to this entry point when it is
+ * having difficulty translating a Dalvik instruction. We must skip
+ * the code cache lookup & prevent chaining to avoid bouncing between
+ * the interpreter and code cache. rPC must be set on entry.
+ */
dvmJitToInterpPunt:
+#if defined(WITH_JIT_TUNING)
+ movl rPC, OUT_ARG0(%esp)
+ call dvmBumpPunt
+#endif
+ FETCH_INST_R %edx
+ GOTO_NEXT_R %edx
+
.global dvmJitToInterpSingleStep
+/*
+ * Return to the interpreter to handle a single instruction.
+ * Should be reached via a call.
+ * On entry:
+ * 0(%esp) <= native return address within trace
+ * rPC <= Dalvik PC of this instruction
+ * OUT_ARG0+4(%esp) <= Dalvik PC of next instruction
+ */
dvmJitToInterpSingleStep:
+ pop %eax
+ movl rGLUE, %ecx
+ movl OUT_ARG0(%esp), %edx
+ movl %eax,offGlue_jitResumeNPC(%ecx)
+ movl %edx,offGlue_jitResumeDPC(%ecx)
+ movl $$kInterpEntryInstr,offGlue_entryPoint(%ecx)
+ movl $$1,rINST # changeInterp <= true
+ jmp common_gotoBail
+
.global dvmJitToInterpNoChainNoProfile
+/*
+ * Return from the translation cache to the interpreter to do method
+ * invocation. Check if the translation exists for the callee, but don't
+ * chain to it. rPC must be set on entry.
+ */
dvmJitToInterpNoChainNoProfile:
+#if defined(WITH_JIT_TUNING)
+ call dvmBumpNoChain
+#endif
+ movl rPC,OUT_ARG0(%esp)
+ call dvmJitGetCodeAddr # is there a translation?
+ movl rGLUE,%ecx
+ movl offGlue_self(%ecx), %ecx # ecx <- glue->self
+ movl %eax,offThread_inJitCodeCache(%ecx) # set inJitCodeCache flag
+ cmpl $$0, %eax
+ jz 1f
+ call *%eax # exec translation if we've got one
+ # won't return
+1:
+ FETCH_INST_R %edx
+ GOTO_NEXT_R %edx
+
+/*
+ * Return from the translation cache and immediately request a
+ * translation fro the exit target, but don't attempt to chain.
+ * rPC set on entry.
+ */
.global dvmJitToInterpTraceSelectNoChain
dvmJitToInterpTraceSelectNoChain:
+#if defined(WITH_JIT_TUNING)
+ call dvmBumpNoChain
+#endif
+ movl rPC,OUT_ARG0(%esp)
+ call dvmJitGetCodeAddr # is there a translation?
+ movl rGLUE,%ecx
+ movl offGlue_self(%ecx),%ecx
+ cmpl $$0,%eax
+ movl %eax,offThread_inJitCodeCache(%ecx) # set inJitCodeCache flag
+ jz 1f
+ call *%eax # jump to tranlation
+ # won't return
+
+/* No Translation - request one */
+1:
+ GET_JIT_PROF_TABLE %ecx %eax
+ cmpl $$0, %eax # JIT enabled?
+ jnz 2f # Request one if so
+ FETCH_INST_R %edx # Continue interpreting if not
+ GOTO_NEXT_R %edx
+2:
+ movl $$kJitTSelectRequestHot,rINST # ask for trace select
+ jmp common_selectTrace
+
+/*
+ * Return from the translation cache and immediately request a
+ * translation for the exit target. Reached via a call, and
+ * (TOS)->rPC.
+ */
.global dvmJitToInterpTraceSelect
dvmJitToInterpTraceSelect:
+ pop rINST # save chain cell address in callee save reg
+ movl (rINST),rPC
+ movl rPC,OUT_ARG0(%esp)
+ call dvmJitGetCodeAddr # is there a translation?
+ cmpl $$0,%eax
+ jz 1b # no - ask for one
+ movl %eax,OUT_ARG0(%esp)
+# FIXME - need to adjust rINST to beginning of sequence
+ movl rINST,OUT_ARG1(%esp)
+ call dvmJitChain # Attempt dvmJitChain(codeAddr,chainAddr)
+ cmpl $$0,%eax # Success?
+ jz toInterpreter # didn't chain - interpret
+ call *%eax
+ # won't return
+
+/*
+ * Placeholder entries for x86 JIT
+ */
.global dvmJitToInterpBackwardBranch
dvmJitToInterpBackwardBranch:
.global dvmJitToInterpNormal
dvmJitToInterpNormal:
.global dvmJitToInterpNoChain
dvmJitToInterpNoChain:
+toInterpreter:
jmp common_abort
#endif
*/
common_backwardBranch:
movl rGLUE,%ecx
- call common_periodicChecks # Note: expects rPC to be preserved
+ call common_periodicChecks # rPC and ecx/rGLUE preserved
+#if defined(WITH_JIT)
+ GET_JIT_PROF_TABLE %ecx %edx
+ ADVANCE_PC_INDEXED rINST
+ cmpl $$0,%edx
+ FETCH_INST
+ jz 1f # Profiling off - continue
+ .global updateProfile
+updateProfile:
+common_updateProfile:
+ # quick & dirty hash
+ movl rPC, %eax
+ shrl $$12, %eax
+ xorl rPC, %eax
+ andl $$((1<<JIT_PROF_SIZE_LOG_2)-1),%eax
+ decb (%edx,%eax)
+ jz 2f
+1:
+ GOTO_NEXT
+2:
+/*
+ * Here, we switch to the debug interpreter to request
+ * trace selection. First, though, check to see if there
+ * is already a native translation in place (and, if so,
+ * jump to it now.
+ */
+ GET_JIT_THRESHOLD %ecx rINST
+ EXPORT_PC
+ movb rINSTbl,(%edx,%eax) # reset counter
+ movl offGlue_self(%ecx),rINST
+ movl rPC,OUT_ARG0(%esp)
+ call dvmJitGetCodeAddr # already have one?
+ movl %eax,offThread_inJitCodeCache(rINST) # set the inJitCodeCache flag
+ cmpl $$0,%eax
+ jz 1f
+ call *%eax # FIXME: decide call vs/ jmp!. No return either way
+1:
+ movl $$kJitTSelectRequest,%eax
+ # On entry, eax<- jitState, rPC valid
+common_selectTrace:
+ movl rGLUE,%ecx
+ movl %eax,offGlue_jitState(%ecx)
+ movl $$kInterpEntryInstr,offGlue_entryPoint(%ecx)
+ movl $$1,rINST
+ jmp common_gotoBail
+#else
ADVANCE_PC_INDEXED rINST
FETCH_INST
GOTO_NEXT
+#endif
Mterp notes:
Some key interpreter variables will be assigned to registers. Note that each
-will also have an associated spill location (mostly used useful for those assigned
+will also have an associated spill location (mostly useful for those assigned
to callee save registers).
nick reg purpose
o High order 16 bits of ebx must be zero on entry to handler
o rPC, rFP, rINSTw/rINSTbl valid on handler entry and exit
o eax, edx and ecx are scratch, rINSTw/ebx sometimes scratch
- o rPC is in the caller save set, and will be killed across external calls. Don't
- forget to SPILL/UNSPILL it around call points
*/
#define SPILL_TMP3(reg) movl reg,TMP_SPILL3(%ebp)
#define UNSPILL_TMP3(reg) movl TMP_SPILL3(%ebp),reg
+#if defined(WITH_JIT)
+.macro GET_JIT_PROF_TABLE _glue _reg
+ movl offGlue_pJitProfTable(\_glue),\_reg
+.endm
+.macro GET_JIT_THRESHOLD _glue _reg
+ movl offGlue_jitThreshold(\_glue),\_reg
+.endm
+#endif
+
/* save/restore the PC and/or FP from the glue struct */
.macro SAVE_PC_FP_TO_GLUE _reg
movl rGLUE,\_reg
* to expand the macros into assembler assignment statements.
*/
#include "../common/asm-constants.h"
+
+#if defined(WITH_JIT)
+#include "../common/jit-config.h"
+#endif
}
/* just in case gid_t and u4 are different... */
- gids = alloca(sizeof(gid_t) * gidArray->length);
+ gids = (gid_t *)alloca(sizeof(gid_t) * gidArray->length);
contents = (s4 *)gidArray->contents;
for (i = 0 ; i < gidArray->length ; i++) {
switch (sfield->field.signature[0]) {
case 'L':
case '[':
- dvmSetStaticFieldObject(sfield, value->l);
+ dvmSetStaticFieldObject(sfield, (Object*)value->l);
break;
default:
/* just copy the whole thing */
break;
case 'L':
case '[':
- dvmSetStaticFieldObjectVolatile(sfield, value->l);
+ dvmSetStaticFieldObjectVolatile(sfield, (Object*)value->l);
break;
default:
LOGE("Unhandled field signature '%s'\n", sfield->field.signature);
break;
case 'L':
case '[':
- dvmSetFieldObject(obj, ifield->byteOffset, value->l);
+ dvmSetFieldObject(obj, ifield->byteOffset, (Object *)value->l);
break;
default:
LOGE("Unhandled field signature '%s'\n", ifield->field.signature);
break;
case 'L':
case '[':
- dvmSetFieldObjectVolatile(obj, ifield->byteOffset, value->l);
+ dvmSetFieldObjectVolatile(obj, ifield->byteOffset, (Object*)value->l);
break;
default:
LOGE("Unhandled field signature '%s'\n", ifield->field.signature);
/* Note that we assume that the Array class does not
* override finalize().
*/
- newArray = dvmMalloc(size, allocFlags);
+ newArray = (ArrayObject*)dvmMalloc(size, allocFlags);
if (newArray != NULL) {
DVM_OBJECT_INIT(&newArray->obj, arrayClass);
newArray->length = length;
case PRIM_BOOLEAN:
case PRIM_BYTE:
{
- u1* tmp = dst;
+ u1* tmp = (u1*)dst;
*tmp++ = result.b;
dst = tmp;
}
case PRIM_CHAR:
case PRIM_SHORT:
{
- u2* tmp = dst;
+ u2* tmp = (u2*)dst;
*tmp++ = result.s;
dst = tmp;
}
case PRIM_FLOAT:
case PRIM_INT:
{
- u4* tmp = dst;
+ u4* tmp = (u4*)dst;
*tmp++ = result.i;
dst = tmp;
}
case PRIM_DOUBLE:
case PRIM_LONG:
{
- u8* tmp = dst;
+ u8* tmp = (u8*)dst;
*tmp++ = result.j;
dst = tmp;
}
static void linearAllocTests()
{
char* fiddle;
- int try = 1;
+ int test = 1;
- switch (try) {
+ switch (test) {
case 0:
- fiddle = dvmLinearAlloc(NULL, 3200-28);
- dvmLinearReadOnly(NULL, fiddle);
+ fiddle = (char*)dvmLinearAlloc(NULL, 3200-28);
+ dvmLinearReadOnly(NULL, (char*)fiddle);
break;
case 1:
- fiddle = dvmLinearAlloc(NULL, 3200-24);
- dvmLinearReadOnly(NULL, fiddle);
+ fiddle = (char*)dvmLinearAlloc(NULL, 3200-24);
+ dvmLinearReadOnly(NULL, (char*)fiddle);
break;
case 2:
- fiddle = dvmLinearAlloc(NULL, 3200-20);
- dvmLinearReadOnly(NULL, fiddle);
+ fiddle = (char*)dvmLinearAlloc(NULL, 3200-20);
+ dvmLinearReadOnly(NULL, (char*)fiddle);
break;
case 3:
- fiddle = dvmLinearAlloc(NULL, 3200-16);
- dvmLinearReadOnly(NULL, fiddle);
+ fiddle = (char*)dvmLinearAlloc(NULL, 3200-16);
+ dvmLinearReadOnly(NULL, (char*)fiddle);
break;
case 4:
- fiddle = dvmLinearAlloc(NULL, 3200-12);
- dvmLinearReadOnly(NULL, fiddle);
+ fiddle = (char*)dvmLinearAlloc(NULL, 3200-12);
+ dvmLinearReadOnly(NULL, (char*)fiddle);
break;
}
- fiddle = dvmLinearAlloc(NULL, 896);
- dvmLinearReadOnly(NULL, fiddle);
- fiddle = dvmLinearAlloc(NULL, 20); // watch addr of this alloc
- dvmLinearReadOnly(NULL, fiddle);
+ fiddle = (char*)dvmLinearAlloc(NULL, 896);
+ dvmLinearReadOnly(NULL, (char*)fiddle);
+ fiddle = (char*)dvmLinearAlloc(NULL, 20); // watch addr of this alloc
+ dvmLinearReadOnly(NULL, (char*)fiddle);
- fiddle = dvmLinearAlloc(NULL, 1);
+ fiddle = (char*)dvmLinearAlloc(NULL, 1);
fiddle[0] = 'q';
dvmLinearReadOnly(NULL, fiddle);
- fiddle = dvmLinearAlloc(NULL, 4096);
+ fiddle = (char*)dvmLinearAlloc(NULL, 4096);
fiddle[0] = 'x';
fiddle[4095] = 'y';
dvmLinearReadOnly(NULL, fiddle);
dvmLinearFree(NULL, fiddle);
- fiddle = dvmLinearAlloc(NULL, 0);
+ fiddle = (char*)dvmLinearAlloc(NULL, 0);
dvmLinearReadOnly(NULL, fiddle);
- fiddle = dvmLinearRealloc(NULL, fiddle, 12);
+ fiddle = (char*)dvmLinearRealloc(NULL, fiddle, 12);
fiddle[11] = 'z';
+ dvmLinearReadOnly(NULL, (char*)fiddle);
+ fiddle = (char*)dvmLinearRealloc(NULL, fiddle, 5);
dvmLinearReadOnly(NULL, fiddle);
- fiddle = dvmLinearRealloc(NULL, fiddle, 5);
- dvmLinearReadOnly(NULL, fiddle);
- fiddle = dvmLinearAlloc(NULL, 17001);
+ fiddle = (char*)dvmLinearAlloc(NULL, 17001);
fiddle[0] = 'x';
fiddle[17000] = 'y';
- dvmLinearReadOnly(NULL, fiddle);
+ dvmLinearReadOnly(NULL, (char*)fiddle);
- char* str = dvmLinearStrdup(NULL, "This is a test!");
+ char* str = (char*)dvmLinearStrdup(NULL, "This is a test!");
LOGI("GOT: '%s'\n", str);
/* try to check the bounds; allocator may round allocation size up */
- fiddle = dvmLinearAlloc(NULL, 12);
+ fiddle = (char*)dvmLinearAlloc(NULL, 12);
LOGI("Should be 1: %d\n", dvmLinearAllocContains(fiddle, 12));
LOGI("Should be 0: %d\n", dvmLinearAllocContains(fiddle, 13));
LOGI("Should be 0: %d\n", dvmLinearAllocContains(fiddle - 128*1024, 1));
dvmLinearAllocDump(NULL);
- dvmLinearFree(NULL, str);
+ dvmLinearFree(NULL, (char*)str);
}
static size_t classObjectSize(size_t sfieldCount)
* If it's NULL, we just fall back to the InitiatingLoaderList in the
* ClassObject, so it's not fatal to fail this allocation.
*/
- gDvm.initiatingLoaderList =
+ gDvm.initiatingLoaderList = (InitiatingLoaderList*)
calloc(ZYGOTE_CLASS_CUTOFF, sizeof(InitiatingLoaderList));
gDvm.classJavaLangClass = (ClassObject*) dvmMalloc(
* here, but this is an extremely rare case, and it's simpler to have
* the wait-for-class code centralized.
*/
- if (found != NULL && !unprepOkay && !dvmIsClassLinked(found)) {
+ if (found && !unprepOkay && !dvmIsClassLinked((ClassObject*)found)) {
LOGV("Ignoring not-yet-ready %s, using slow path\n",
((ClassObject*)found)->descriptor);
found = NULL;
*/
assert(sizeof(*interfaceIdxArray) == sizeof(*clazz->interfaces));
size_t len = clazz->interfaceCount * sizeof(*interfaceIdxArray);
- interfaceIdxArray = malloc(len);
+ interfaceIdxArray = (u4*)malloc(len);
if (interfaceIdxArray == NULL) {
LOGW("Unable to allocate memory to link %s", clazz->descriptor);
goto bail;
if (actualCount < maxCount) {
assert(clazz->vtable != NULL);
dvmLinearReadOnly(clazz->classLoader, clazz->vtable);
- clazz->vtable = dvmLinearRealloc(clazz->classLoader, clazz->vtable,
- sizeof(*(clazz->vtable)) * actualCount);
+ clazz->vtable = (Method **)dvmLinearRealloc(clazz->classLoader,
+ clazz->vtable, sizeof(*(clazz->vtable)) * actualCount);
if (clazz->vtable == NULL) {
LOGE("vtable realloc failed\n");
goto bail;
if (mirandaCount == mirandaAlloc) {
mirandaAlloc += 8;
if (mirandaList == NULL) {
- mirandaList = dvmLinearAlloc(clazz->classLoader,
+ mirandaList = (Method**)dvmLinearAlloc(
+ clazz->classLoader,
mirandaAlloc * sizeof(Method*));
} else {
dvmLinearReadOnly(clazz->classLoader, mirandaList);
- mirandaList = dvmLinearRealloc(clazz->classLoader,
+ mirandaList = (Method**)dvmLinearRealloc(
+ clazz->classLoader,
mirandaList, mirandaAlloc * sizeof(Method*));
}
assert(mirandaList != NULL); // mem failed + we leaked
* All's well, so store the value.
*/
if (isObj) {
- dvmSetStaticFieldObject(sfield, value.value.l);
- dvmReleaseTrackedAlloc(value.value.l, self);
+ dvmSetStaticFieldObject(sfield, (Object*)value.value.l);
+ dvmReleaseTrackedAlloc((Object*)value.value.l, self);
} else {
/*
* Note: This always stores the full width of a
/* update both, ensuring that "insns" is observed first */
method->insns = insns;
android_atomic_release_store((int32_t) func,
- (void*) &method->nativeFunc);
+ (volatile int32_t*)(void*) &method->nativeFunc);
} else {
/* only update nativeFunc */
method->nativeFunc = func;
*/
static int findClassCallback(void* vclazz, void* arg)
{
- ClassObject* clazz = vclazz;
+ ClassObject* clazz = (ClassObject*)vclazz;
const char* descriptor = (const char*) arg;
if (strcmp(clazz->descriptor, descriptor) == 0)
*/
INLINE void dvmSetObjectArrayElement(const ArrayObject* obj, int index,
Object* val) {
- ((Object **)(obj)->contents)[index] = val;
+ ((Object **)(void *)(obj)->contents)[index] = val;
if (val != NULL) {
dvmWriteBarrierArray(obj, index, index + 1);
}
return ((JValue*)BYTE_OFFSET(obj, offset))->d;
}
INLINE Object* dvmGetFieldObject(const Object* obj, int offset) {
- return ((JValue*)BYTE_OFFSET(obj, offset))->l;
+ return (Object*)((JValue*)BYTE_OFFSET(obj, offset))->l;
}
INLINE bool dvmGetFieldBooleanVolatile(const Object* obj, int offset) {
s4* ptr = &((JValue*)BYTE_OFFSET(obj, offset))->i;
return alias.fval;
}
INLINE s8 dvmGetFieldLongVolatile(const Object* obj, int offset) {
- const s8* addr = BYTE_OFFSET(obj, offset);
+ const s8* addr = (const s8*)BYTE_OFFSET(obj, offset);
s8 val = dvmQuasiAtomicRead64(addr);
ANDROID_MEMBAR_FULL();
return val;
}
INLINE double dvmGetFieldDoubleVolatile(const Object* obj, int offset) {
union { s8 lval; double dval; } alias;
- const s8* addr = BYTE_OFFSET(obj, offset);
+ const s8* addr = (const s8*)BYTE_OFFSET(obj, offset);
alias.lval = dvmQuasiAtomicRead64(addr);
ANDROID_MEMBAR_FULL();
return alias.dval;
((JValue*)BYTE_OFFSET(obj, offset))->d = val;
}
INLINE void dvmSetFieldObject(Object* obj, int offset, Object* val) {
- JValue* lhs = BYTE_OFFSET(obj, offset);
+ JValue* lhs = (JValue*)BYTE_OFFSET(obj, offset);
lhs->l = val;
if (val != NULL) {
dvmWriteBarrierField(obj, &lhs->l);
dvmSetFieldIntVolatile(obj, offset, alias.ival);
}
INLINE void dvmSetFieldLongVolatile(Object* obj, int offset, s8 val) {
- s8* addr = BYTE_OFFSET(obj, offset);
+ s8* addr = (s8*)BYTE_OFFSET(obj, offset);
ANDROID_MEMBAR_FULL();
dvmQuasiAtomicSwap64(val, addr);
}
return sfield->value.d;
}
INLINE Object* dvmGetStaticFieldObject(const StaticField* sfield) {
- return sfield->value.l;
+ return (Object*)sfield->value.l;
}
INLINE bool dvmGetStaticFieldBooleanVolatile(const StaticField* sfield) {
const s4* ptr = &(sfield->value.i);
return false;
} else {
assert(sfield->field.clazz->descriptor[0] == 'L');
- elemObj = sfield->value.l;
+ elemObj = (Object*)sfield->value.l;
setObject = true;
dvmAddTrackedAlloc(elemObj, self); // balance the Release
}
dvmReleaseTrackedAlloc((Object*)newArray, self);
return false;
}
- Object* obj = avalue.value.l;
+ Object* obj = (Object*)avalue.value.l;
dvmSetObjectArrayElement(newArray, count, obj);
dvmReleaseTrackedAlloc(obj, self);
}
LOGW("Failed processing annotation value\n");
goto bail;
}
- valueObj = avalue.value.l;
+ valueObj = (Object*)avalue.value.l;
/* new member to hold the element */
newMember =
goto bail;
}
- newAnno = result.l;
+ newAnno = (Object*)result.l;
bail:
dvmReleaseTrackedAlloc((Object*) elementArray, NULL);
return GAV_FAILED;
}
- return avalue.value.l;
+ return (Object*)avalue.value.l;
}
/* convert the return type, if necessary */
ClassObject* methodReturn = dvmGetBoxedReturnType(method);
- Object* obj = avalue.value.l;
+ Object* obj = (Object*)avalue.value.l;
obj = convertReturnType(obj, methodReturn);
return obj;
/* grab a local copy to work on */
for (i = 0; i < mixLen; i++) {
- mixSet[i] = dvmPointerSetGetEntry(throws, i);
+ mixSet[i] = (ClassObject*)dvmPointerSetGetEntry(throws, i);
}
for (i = 0; i < mixLen; i++) {
}
pResult->l = NULL;
} else {
- if (!dvmUnboxPrimitive(invokeResult.l, returnType, pResult)) {
+ if (!dvmUnboxPrimitive((Object*)invokeResult.l, returnType, pResult)) {
dvmThrowExceptionWithClassMessage("Ljava/lang/ClassCastException;",
((Object*)invokeResult.l)->clazz->descriptor);
goto bail;
if (typeIndex == PRIM_NOT) {
/* add to tracking table so return value is always in table */
if (value.l != NULL)
- dvmAddTrackedAlloc(value.l, NULL);
+ dvmAddTrackedAlloc((Object*)value.l, NULL);
return (DataObject*) value.l;
}
/* two entries, same hash, different values */
char* str1;
- str1 = dvmHashTableLookup(pTab, hash, strdup("one"),
+ str1 = (char*) dvmHashTableLookup(pTab, hash, strdup("one"),
(HashCompareFunc) strcmp, true);
assert(str1 != NULL);
- str = dvmHashTableLookup(pTab, hash, strdup("two"),
+ str = (const char*) dvmHashTableLookup(pTab, hash, strdup("two"),
(HashCompareFunc) strcmp, true);
/* remove the first one */
}
/* see if we can find them */
- str = dvmHashTableLookup(pTab, hash, "one", (HashCompareFunc) strcmp,false);
+ str = (const char*) dvmHashTableLookup(pTab, hash, "one",
+ (HashCompareFunc) strcmp,false);
if (str != NULL)
LOGE("TestHash deleted entry has returned!");
- str = dvmHashTableLookup(pTab, hash, "two", (HashCompareFunc) strcmp,false);
+ str = (const char*) dvmHashTableLookup(pTab, hash, "two",
+ (HashCompareFunc) strcmp,false);
if (str == NULL)
LOGE("TestHash entry vanished\n");