diff options
author | Jeff Hao <jeffhao@google.com> | 2015-10-07 23:27:55 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2015-10-07 23:27:55 +0000 |
commit | 32b6d346bf801ac8051f6e9d6c632af6ebd3856a (patch) | |
tree | b07c964f450ef0ee3370eaaa5651a58c941f5c3b | |
parent | ca2f25be24c0d5bba13ddf3b2aa60f205006eec6 (diff) | |
parent | 4658eeda87e63a7ad4ba5a00f429ba4ee10a5162 (diff) | |
download | dalvik-32b6d346bf801ac8051f6e9d6c632af6ebd3856a.tar.gz |
Merge "Remove dmtracedump from dalvik."
-rw-r--r-- | tools/dmtracedump/Android.mk | 21 | ||||
-rw-r--r-- | tools/dmtracedump/CreateTestTrace.c | 493 | ||||
-rw-r--r-- | tools/dmtracedump/Profile.h | 43 | ||||
-rw-r--r-- | tools/dmtracedump/TraceDump.c | 2910 | ||||
-rwxr-xr-x | tools/dmtracedump/dmtracedump.pl | 18 | ||||
-rw-r--r-- | tools/dmtracedump/dumpdir.sh | 11 |
6 files changed, 0 insertions, 3496 deletions
diff --git a/tools/dmtracedump/Android.mk b/tools/dmtracedump/Android.mk deleted file mode 100644 index 908be5f90..000000000 --- a/tools/dmtracedump/Android.mk +++ /dev/null @@ -1,21 +0,0 @@ -# -# Copyright 2006 The Android Open Source Project -# -# Java method trace dump tool -# - -LOCAL_PATH:= $(call my-dir) - - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := TraceDump.c -LOCAL_CFLAGS += -O0 -g -Wall -LOCAL_MODULE_HOST_OS := darwin linux windows -LOCAL_MODULE := dmtracedump -include $(BUILD_HOST_EXECUTABLE) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := CreateTestTrace.c -LOCAL_CFLAGS += -O0 -g -Wall -LOCAL_MODULE := create_test_dmtrace -include $(BUILD_HOST_EXECUTABLE) diff --git a/tools/dmtracedump/CreateTestTrace.c b/tools/dmtracedump/CreateTestTrace.c deleted file mode 100644 index a4ae02e7b..000000000 --- a/tools/dmtracedump/CreateTestTrace.c +++ /dev/null @@ -1,493 +0,0 @@ -/* //device/tools/dmtracedump/CreateTrace.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. -*/ - -/* - * Create a test file in the format required by dmtrace. - */ -#define NOT_VM -#include "Profile.h" // from VM header - -#include <string.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <assert.h> -#include <unistd.h> -#include <sys/time.h> -#include <time.h> -#include <ctype.h> - -/* - * Values from the header of the data file. - */ -typedef struct DataHeader { - unsigned int magic; - short version; - short offsetToData; - long long startWhen; -} DataHeader; - -#define VERSION 2 -int versionNumber = VERSION; -int verbose = 0; - -DataHeader header = { 0x574f4c53, VERSION, sizeof(DataHeader), 0LL }; - -char *versionHeader = "*version\n"; -char *clockDef = "clock=thread-cpu\n"; - -char *keyThreads = -"*threads\n" -"1 main\n" -"2 foo\n" -"3 bar\n" -"4 blah\n"; - -char *keyEnd = "*end\n"; - -typedef struct dataRecord { - unsigned int time; - int threadId; - unsigned int action; /* 0=entry, 1=exit, 2=exception exit */ - char *fullName; - char *className; - char *methodName; - char *signature; - unsigned int methodId; -} dataRecord; - -dataRecord *records; - -#define BUF_SIZE 1024 -char buf[BUF_SIZE]; - -typedef struct stack { - dataRecord **frames; - int indentLevel; -} stack; - -/* Mac OS doesn't have strndup(), so implement it here. - */ -char *strndup(const char *src, size_t len) -{ - char *dest = (char *) malloc(len + 1); - strncpy(dest, src, len); - dest[len] = 0; - return dest; -} - -/* - * Parse the input file. It looks something like this: - * # This is a comment line - * 4 1 A - * 6 1 B - * 8 1 B - * 10 1 A - * - * where the first column is the time, the second column is the thread id, - * and the third column is the method (actually just the class name). The - * number of spaces between the 2nd and 3rd columns is the indentation and - * determines the call stack. Each called method must be indented by one - * more space. In the example above, A is called at time 4, A calls B at - * time 6, B returns at time 8, and A returns at time 10. Thread 1 is the - * only thread that is running. - * - * An alternative file format leaves out the first two columns: - * A - * B - * B - * A - * - * In this file format, the thread id is always 1, and the time starts at - * 2 and increments by 2 for each line. - */ -void parseInputFile(const char *inputFileName) -{ - unsigned int time = 0, threadId; - int len; - int linenum = 0; - int nextRecord = 0; - int indentLevel = 0; - stack *callStack; - - FILE *inputFp = fopen(inputFileName, "r"); - if (inputFp == NULL) { - perror(inputFileName); - exit(1); - } - - /* Count the number of lines in the buffer */ - int numRecords = 0; - int maxThreadId = 1; - int maxFrames = 0; - char *indentEnd; - while (fgets(buf, BUF_SIZE, inputFp)) { - char *cp = buf; - if (*cp == '#') - continue; - numRecords += 1; - if (isdigit(*cp)) { - int time = strtoul(cp, &cp, 0); - while (isspace(*cp)) - cp += 1; - int threadId = strtoul(cp, &cp, 0); - if (maxThreadId < threadId) - maxThreadId = threadId; - } - indentEnd = cp; - while (isspace(*indentEnd)) - indentEnd += 1; - if (indentEnd - cp + 1 > maxFrames) - maxFrames = indentEnd - cp + 1; - } - int numThreads = maxThreadId + 1; - - /* Add space for a sentinel record at the end */ - numRecords += 1; - records = (dataRecord *) malloc(sizeof(dataRecord) * numRecords); - callStack = (stack *) malloc(sizeof(stack) * numThreads); - int ii; - for (ii = 0; ii < numThreads; ++ii) { - callStack[ii].frames = NULL; - callStack[ii].indentLevel = 0; - } - - rewind(inputFp); - while (fgets(buf, BUF_SIZE, inputFp)) { - int indent; - int action; - char *save_cp; - - linenum += 1; - char *cp = buf; - - /* Skip lines that start with '#' */ - if (*cp == '#') - continue; - - /* Get time and thread id */ - if (!isdigit(*cp)) { - /* If the line does not begin with a digit, then fill in - * default values for the time and threadId. - */ - time += 2; - threadId = 1; - } else { - time = strtoul(cp, &cp, 0); - while (isspace(*cp)) - cp += 1; - threadId = strtoul(cp, &cp, 0); - cp += 1; - } - - // Allocate space for the thread stack, if necessary - if (callStack[threadId].frames == NULL) { - dataRecord **stk; - stk = (dataRecord **) malloc(sizeof(dataRecord *) * maxFrames); - callStack[threadId].frames = stk; - } - indentLevel = callStack[threadId].indentLevel; - - save_cp = cp; - while (isspace(*cp)) { - cp += 1; - } - indent = cp - save_cp + 1; - records[nextRecord].time = time; - records[nextRecord].threadId = threadId; - - save_cp = cp; - while (*cp != '\n') - cp += 1; - - /* Remove trailing spaces */ - cp -= 1; - while (isspace(*cp)) - cp -= 1; - cp += 1; - len = cp - save_cp; - records[nextRecord].fullName = strndup(save_cp, len); - - /* Parse the name to support "class.method signature" */ - records[nextRecord].className = NULL; - records[nextRecord].methodName = NULL; - records[nextRecord].signature = NULL; - cp = strchr(save_cp, '.'); - if (cp) { - len = cp - save_cp; - if (len > 0) - records[nextRecord].className = strndup(save_cp, len); - save_cp = cp + 1; - cp = strchr(save_cp, ' '); - if (cp == NULL) - cp = strchr(save_cp, '\n'); - if (cp && cp > save_cp) { - len = cp - save_cp; - records[nextRecord].methodName = strndup(save_cp, len); - save_cp = cp + 1; - cp = strchr(save_cp, ' '); - if (cp == NULL) - cp = strchr(save_cp, '\n'); - if (cp && cp > save_cp) { - len = cp - save_cp; - records[nextRecord].signature = strndup(save_cp, len); - } - } - } - - if (verbose) { - printf("Indent: %d; IndentLevel: %d; Line: %s", indent, indentLevel, buf); - } - - action = 0; - if (indent == indentLevel + 1) { // Entering a method - if (verbose) - printf(" Entering %s\n", records[nextRecord].fullName); - callStack[threadId].frames[indentLevel] = &records[nextRecord]; - } else if (indent == indentLevel) { // Exiting a method - // Exiting method must be currently on top of stack (unless stack is empty) - if (callStack[threadId].frames[indentLevel - 1] == NULL) { - if (verbose) - printf(" Exiting %s (past bottom of stack)\n", records[nextRecord].fullName); - callStack[threadId].frames[indentLevel - 1] = &records[nextRecord]; - action = 1; - } else { - if (indentLevel < 1) { - fprintf(stderr, "Error: line %d: %s", linenum, buf); - fprintf(stderr, " expected positive (>0) indentation, found %d\n", - indent); - exit(1); - } - char *name = callStack[threadId].frames[indentLevel - 1]->fullName; - if (strcmp(name, records[nextRecord].fullName) == 0) { - if (verbose) - printf(" Exiting %s\n", name); - action = 1; - } else { // exiting method doesn't match stack's top method - fprintf(stderr, "Error: line %d: %s", linenum, buf); - fprintf(stderr, " expected exit from %s\n", - callStack[threadId].frames[indentLevel - 1]->fullName); - exit(1); - } - } - } else { - if (nextRecord != 0) { - fprintf(stderr, "Error: line %d: %s", linenum, buf); - fprintf(stderr, " expected indentation %d [+1], found %d\n", - indentLevel, indent); - exit(1); - } - - if (verbose) { - printf(" Nonzero indent at first record\n"); - printf(" Entering %s\n", records[nextRecord].fullName); - } - - // This is the first line of data, so we allow a larger - // initial indent. This allows us to test popping off more - // frames than we entered. - indentLevel = indent - 1; - callStack[threadId].frames[indentLevel] = &records[nextRecord]; - } - - if (action == 0) - indentLevel += 1; - else - indentLevel -= 1; - records[nextRecord].action = action; - callStack[threadId].indentLevel = indentLevel; - - nextRecord += 1; - } - - /* Mark the last record with a sentinel */ - memset(&records[nextRecord], 0, sizeof(dataRecord)); -} - - -/* - * Write values to the binary data file. - */ -void write2LE(FILE* fp, unsigned short val) -{ - putc(val & 0xff, fp); - putc(val >> 8, fp); -} - -void write4LE(FILE* fp, unsigned int val) -{ - putc(val & 0xff, fp); - putc((val >> 8) & 0xff, fp); - putc((val >> 16) & 0xff, fp); - putc((val >> 24) & 0xff, fp); -} - -void write8LE(FILE* fp, unsigned long long val) -{ - putc(val & 0xff, fp); - putc((val >> 8) & 0xff, fp); - putc((val >> 16) & 0xff, fp); - putc((val >> 24) & 0xff, fp); - putc((val >> 32) & 0xff, fp); - putc((val >> 40) & 0xff, fp); - putc((val >> 48) & 0xff, fp); - putc((val >> 56) & 0xff, fp); -} - -void writeDataRecord(FILE *dataFp, int threadId, unsigned int methodVal, - unsigned int elapsedTime) -{ - if (versionNumber == 1) - putc(threadId, dataFp); - else - write2LE(dataFp, threadId); - write4LE(dataFp, methodVal); - write4LE(dataFp, elapsedTime); -} - -void writeDataHeader(FILE *dataFp) -{ - struct timeval tv; - struct timezone tz; - - gettimeofday(&tv, &tz); - unsigned long long startTime = tv.tv_sec; - startTime = (startTime << 32) | tv.tv_usec; - header.version = versionNumber; - write4LE(dataFp, header.magic); - write2LE(dataFp, header.version); - write2LE(dataFp, header.offsetToData); - write8LE(dataFp, startTime); -} - -void writeKeyMethods(FILE *keyFp) -{ - dataRecord *pRecord, *pNext; - char *methodStr = "*methods\n"; - fwrite(methodStr, strlen(methodStr), 1, keyFp); - - /* Assign method ids in multiples of 4 */ - unsigned int methodId = 0; - for (pRecord = records; pRecord->fullName; ++pRecord) { - if (pRecord->methodId) - continue; - unsigned int id = ++methodId << 2; - pRecord->methodId = id; - - /* Assign this id to all the other records that have the - * same name. - */ - for (pNext = pRecord + 1; pNext->fullName; ++pNext) { - if (pNext->methodId) - continue; - if (strcmp(pRecord->fullName, pNext->fullName) == 0) - pNext->methodId = id; - } - if (pRecord->className == NULL || pRecord->methodName == NULL) { - fprintf(keyFp, "%#x %s m ()\n", - pRecord->methodId, pRecord->fullName); - } else if (pRecord->signature == NULL) { - fprintf(keyFp, "%#x %s %s ()\n", - pRecord->methodId, pRecord->className, - pRecord->methodName); - } else { - fprintf(keyFp, "%#x %s %s %s\n", - pRecord->methodId, pRecord->className, - pRecord->methodName, pRecord->signature); - } - } -} - -void writeKeys(FILE *keyFp) -{ - fprintf(keyFp, "%s%d\n%s", versionHeader, versionNumber, clockDef); - fwrite(keyThreads, strlen(keyThreads), 1, keyFp); - writeKeyMethods(keyFp); - fwrite(keyEnd, strlen(keyEnd), 1, keyFp); -} - -void writeDataRecords(FILE *dataFp) -{ - dataRecord *pRecord; - - for (pRecord = records; pRecord->fullName; ++pRecord) { - unsigned int val = METHOD_COMBINE(pRecord->methodId, pRecord->action); - writeDataRecord(dataFp, pRecord->threadId, val, pRecord->time); - } -} - -void writeTrace(const char* traceFileName) -{ - FILE *fp = fopen(traceFileName, "w"); - if (fp == NULL) { - perror(traceFileName); - exit(1); - } - writeKeys(fp); - writeDataHeader(fp); - writeDataRecords(fp); - fclose(fp); -} - -int parseOptions(int argc, char **argv) -{ - int err = 0; - while (1) { - int opt = getopt(argc, argv, "v:d"); - if (opt == -1) - break; - switch (opt) { - case 'v': - versionNumber = strtoul(optarg, NULL, 0); - if (versionNumber != 1 && versionNumber != 2) { - fprintf(stderr, "Error: version number (%d) must be 1 or 2\n", - versionNumber); - err = 1; - } - break; - case 'd': - verbose = 1; - break; - default: - err = 1; - break; - } - } - return err; -} - -int main(int argc, char** argv) -{ - char *inputFile; - char *traceFileName = NULL; - int len; - - if (parseOptions(argc, argv) || argc - optind != 2) { - fprintf(stderr, "Usage: %s [-v version] [-d] input_file trace_prefix\n", - argv[0]); - exit(1); - } - - inputFile = argv[optind++]; - parseInputFile(inputFile); - traceFileName = argv[optind++]; - - writeTrace(traceFileName); - - return 0; -} diff --git a/tools/dmtracedump/Profile.h b/tools/dmtracedump/Profile.h deleted file mode 100644 index efbedb35e..000000000 --- a/tools/dmtracedump/Profile.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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. - */ - -/* - * Android's method call profiling goodies. - */ -#ifndef DALVIK_PROFILE_H_ -#define DALVIK_PROFILE_H_ - -/* - * Enumeration for the two "action" bits. - */ -enum { - METHOD_TRACE_ENTER = 0x00, // method entry - METHOD_TRACE_EXIT = 0x01, // method exit - METHOD_TRACE_UNROLL = 0x02, // method exited by exception unrolling - // 0x03 currently unused -}; - -#define TOKEN_CHAR '*' - -/* - * Common definitions, shared with the dump tool. - */ -#define METHOD_ACTION_MASK 0x03 /* two bits */ -#define METHOD_ID(_method) ((_method) & (~METHOD_ACTION_MASK)) -#define METHOD_ACTION(_method) (((unsigned int)(_method)) & METHOD_ACTION_MASK) -#define METHOD_COMBINE(_method, _action) ((_method) | (_action)) - -#endif // DALVIK_PROFILE_H_ diff --git a/tools/dmtracedump/TraceDump.c b/tools/dmtracedump/TraceDump.c deleted file mode 100644 index 2df38f97b..000000000 --- a/tools/dmtracedump/TraceDump.c +++ /dev/null @@ -1,2910 +0,0 @@ -/* - * 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. - * - * This is the wrong way to go about it -- C is a clumsy language for - * shuffling data around. It'll do for a first pass. - */ -#define NOT_VM -#include "Profile.h" // from VM header - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <inttypes.h> -#include <time.h> -#include <errno.h> -#include <assert.h> - -#ifndef __unused /* sys/cdefs.h not available on all platforms */ -# define __unused __attribute__((__unused__)) -#endif - -/* Version number in the key file. - * Version 1 uses one byte for the thread id. - * Version 2 uses two bytes for the thread ids. - * Version 3 encodes the record size and adds an optional extra timestamp field. - */ -int versionNumber; - -/* arbitrarily limit indentation */ -#define MAX_STACK_DEPTH 10000 - -/* thread list in key file is not reliable, so just max out */ -#define MAX_THREADS 32768 - -/* Size of temporary buffers for escaping html strings */ -#define HTML_BUFSIZE 10240 - -char *htmlHeader = -"<html>\n<head>\n<script type=\"text/javascript\" src=\"%ssortable.js\"></script>\n" -"<script langugage=\"javascript\">\n" -"function toggle(item) {\n" -" obj=document.getElementById(item);\n" -" visible=(obj.style.display!=\"none\" && obj.style.display!=\"\");\n" -" key=document.getElementById(\"x\" + item);\n" -" if (visible) {\n" -" obj.style.display=\"none\";\n" -" key.innerHTML=\"+\";\n" -" } else {\n" -" obj.style.display=\"block\";\n" -" key.innerHTML=\"-\";\n" -" }\n" -"}\n" -"function onMouseOver(obj) {\n" -" obj.style.background=\"lightblue\";\n" -"}\n" -"function onMouseOut(obj) {\n" -" obj.style.background=\"white\";\n" -"}\n" -"</script>\n" -"<style type=\"text/css\">\n" -"div { font-family: courier; font-size: 13 }\n" -"div.parent { margin-left: 15; display: none }\n" -"div.leaf { margin-left: 10 }\n" -"div.header { margin-left: 10 }\n" -"div.link { margin-left: 10; cursor: move }\n" -"span.parent { padding-right: 10; }\n" -"span.leaf { padding-right: 10; }\n" -"a img { border: 0;}\n" -"table.sortable th { border-width: 0px 1px 1px 1px; background-color: #ccc;}\n" -"a { text-decoration: none; }\n" -"a:hover { text-decoration: underline; }\n" -"table.sortable th, table.sortable td { text-align: left;}" -"table.sortable tr.odd td { background-color: #ddd; }\n" -"table.sortable tr.even td { background-color: #fff; }\n" -"</style>\n" -"</head><body>\n\n"; - -char *htmlFooter = "\n</body>\n</html>\n"; -char *profileSeparator = - "======================================================================"; - -const char* tableHeader = - "<table class='sortable' id='%s'><tr>\n" - "<th>Method</th>\n" - "<th>Run 1 (us)</th>\n" - "<th>Run 2 (us)</th>\n" - "<th>Diff (us)</th>\n" - "<th>Diff (%%)</th>\n" - "<th>1: # calls</th>\n" - "<th>2: # calls</th>\n" - "</tr>\n"; - -const char* tableHeaderMissing = - "<table class='sortable' id='%s'>\n" - "<th>Method</th>\n" - "<th>Exclusive</th>\n" - "<th>Inclusive</th>\n" - "<th># calls</th>\n"; - -#define GRAPH_LABEL_VISITED 0x0001 -#define GRAPH_NODE_VISITED 0x0002 - -/* - * Values from the header of the data file. - */ -typedef struct DataHeader { - unsigned int magic; - short version; - short offsetToData; - long long startWhen; - short recordSize; -} DataHeader; - -/* - * Entry from the thread list. - */ -typedef struct ThreadEntry { - int threadId; - const char* threadName; -} ThreadEntry; - -struct MethodEntry; -typedef struct TimedMethod { - struct TimedMethod *next; - uint64_t elapsedInclusive; - int numCalls; - struct MethodEntry *method; -} TimedMethod; - -typedef struct ClassEntry { - const char *className; - uint64_t elapsedExclusive; - int numMethods; - struct MethodEntry **methods; /* list of methods in this class */ - int numCalls[2]; /* 0=normal, 1=recursive */ -} ClassEntry; - -typedef struct UniqueMethodEntry { - uint64_t elapsedExclusive; - int numMethods; - struct MethodEntry **methods; /* list of methods with same name */ - int numCalls[2]; /* 0=normal, 1=recursive */ -} UniqueMethodEntry; - -/* - * Entry from the method list. - */ -typedef struct MethodEntry { - int64_t methodId; - const char* className; - const char* methodName; - const char* signature; - const char* fileName; - int lineNum; - uint64_t elapsedExclusive; - uint64_t elapsedInclusive; - uint64_t topExclusive; /* non-recursive exclusive time */ - uint64_t recursiveInclusive; - struct TimedMethod *parents[2]; /* 0=normal, 1=recursive */ - struct TimedMethod *children[2]; /* 0=normal, 1=recursive */ - int numCalls[2]; /* 0=normal, 1=recursive */ - int index; /* used after sorting to number methods */ - int recursiveEntries; /* number of entries on the stack */ - int graphState; /* used when graphing to see if this method has been visited before */ -} MethodEntry; - -/* - * The parsed contents of the key file. - */ -typedef struct DataKeys { - char* fileData; /* contents of the entire file */ - long fileLen; - int numThreads; - ThreadEntry* threads; - int numMethods; - MethodEntry* methods; /* 2 extra methods: "toplevel" and "unknown" */ -} DataKeys; - -#define TOPLEVEL_INDEX 0 -#define UNKNOWN_INDEX 1 - -typedef struct StackEntry { - MethodEntry *method; - uint64_t entryTime; -} StackEntry; - -typedef struct CallStack { - int top; - StackEntry calls[MAX_STACK_DEPTH]; - uint64_t lastEventTime; - uint64_t threadStartTime; -} CallStack; - -typedef struct DiffEntry { - MethodEntry* method1; - MethodEntry* method2; - int64_t differenceExclusive; - int64_t differenceInclusive; - double differenceExclusivePercentage; - double differenceInclusivePercentage; -} DiffEntry; - -// Global options -typedef struct Options { - const char* traceFileName; - const char* diffFileName; - const char* graphFileName; - int keepDotFile; - int dump; - int outputHtml; - const char* sortableUrl; - int threshold; -} Options; - -typedef struct TraceData { - int numClasses; - ClassEntry *classes; - CallStack *stacks[MAX_THREADS]; - int depth[MAX_THREADS]; - int numUniqueMethods; - UniqueMethodEntry *uniqueMethods; -} TraceData; - -static Options gOptions; - -/* Escapes characters in the source string that are html special entities. - * The escaped string is written to "dest" which must be large enough to - * hold the result. A pointer to "dest" is returned. The characters and - * their corresponding escape sequences are: - * '<' < - * '>' > - * '&' & - */ -char *htmlEscape(const char *src, char *dest, int len) -{ - char *destStart = dest; - - if (src == NULL) - return NULL; - - int nbytes = 0; - while (*src) { - if (*src == '<') { - nbytes += 4; - if (nbytes >= len) - break; - *dest++ = '&'; - *dest++ = 'l'; - *dest++ = 't'; - *dest++ = ';'; - } else if (*src == '>') { - nbytes += 4; - if (nbytes >= len) - break; - *dest++ = '&'; - *dest++ = 'g'; - *dest++ = 't'; - *dest++ = ';'; - } else if (*src == '&') { - nbytes += 5; - if (nbytes >= len) - break; - *dest++ = '&'; - *dest++ = 'a'; - *dest++ = 'm'; - *dest++ = 'p'; - *dest++ = ';'; - } else { - nbytes += 1; - if (nbytes >= len) - break; - *dest++ = *src; - } - src += 1; - } - if (nbytes >= len) { - fprintf(stderr, "htmlEscape(): buffer overflow\n"); - exit(1); - } - *dest = 0; - - return destStart; -} - -/* Initializes a MethodEntry - */ -void initMethodEntry(MethodEntry *method, int64_t methodId, - const char *className, const char *methodName, - const char *signature, const char* fileName, - const char* lineNumStr) -{ - method->methodId = methodId; - method->className = className; - method->methodName = methodName; - method->signature = signature; - method->fileName = fileName; - method->lineNum = (lineNumStr != NULL) ? atoi(lineNumStr) : -1; - method->elapsedExclusive = 0; - method->elapsedInclusive = 0; - method->topExclusive = 0; - method->recursiveInclusive = 0; - method->parents[0] = NULL; - method->parents[1] = NULL; - method->children[0] = NULL; - method->children[1] = NULL; - method->numCalls[0] = 0; - method->numCalls[1] = 0; - method->index = 0; - method->recursiveEntries = 0; -} - -/* - * This comparison function is called from qsort() to sort - * methods into decreasing order of exclusive elapsed time. - */ -int compareElapsedExclusive(const void *a, const void *b) { - uint64_t elapsed1, elapsed2; - int result; - - const MethodEntry *methodA = *(const MethodEntry**)a; - const MethodEntry *methodB = *(const MethodEntry**)b; - elapsed1 = methodA->elapsedExclusive; - elapsed2 = methodB->elapsedExclusive; - if (elapsed1 < elapsed2) - return 1; - if (elapsed1 > elapsed2) - return -1; - - /* If the elapsed times of two methods are equal, then sort them - * into alphabetical order. - */ - result = strcmp(methodA->className, methodB->className); - if (result == 0) { - if (methodA->methodName == NULL || methodB->methodName == NULL) { - int64_t idA = methodA->methodId; - int64_t idB = methodB->methodId; - if (idA < idB) - return -1; - if (idA > idB) - return 1; - return 0; - } - result = strcmp(methodA->methodName, methodB->methodName); - if (result == 0) - result = strcmp(methodA->signature, methodB->signature); - } - return result; -} - -/* - * This comparison function is called from qsort() to sort - * methods into decreasing order of inclusive elapsed time. - */ -int compareElapsedInclusive(const void *a, const void *b) { - const MethodEntry *methodA, *methodB; - uint64_t elapsed1, elapsed2; - int result; - - methodA = *(MethodEntry const **)a; - methodB = *(MethodEntry const **)b; - elapsed1 = methodA->elapsedInclusive; - elapsed2 = methodB->elapsedInclusive; - if (elapsed1 < elapsed2) - return 1; - if (elapsed1 > elapsed2) - return -1; - - /* If the elapsed times of two methods are equal, then sort them - * into alphabetical order. - */ - result = strcmp(methodA->className, methodB->className); - if (result == 0) { - if (methodA->methodName == NULL || methodB->methodName == NULL) { - int64_t idA = methodA->methodId; - int64_t idB = methodB->methodId; - if (idA < idB) - return -1; - if (idA > idB) - return 1; - return 0; - } - result = strcmp(methodA->methodName, methodB->methodName); - if (result == 0) - result = strcmp(methodA->signature, methodB->signature); - } - 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) { - const TimedMethod *timedA, *timedB; - uint64_t elapsed1, elapsed2; - int result; - - timedA = (TimedMethod const *)a; - timedB = (TimedMethod const *)b; - elapsed1 = timedA->elapsedInclusive; - elapsed2 = timedB->elapsedInclusive; - if (elapsed1 < elapsed2) - return 1; - if (elapsed1 > elapsed2) - return -1; - - /* If the elapsed times of two methods are equal, then sort them - * into alphabetical order. - */ - MethodEntry *methodA = timedA->method; - MethodEntry *methodB = timedB->method; - result = strcmp(methodA->className, methodB->className); - if (result == 0) { - if (methodA->methodName == NULL || methodB->methodName == NULL) { - int64_t idA = methodA->methodId; - int64_t idB = methodB->methodId; - if (idA < idB) - return -1; - if (idA > idB) - return 1; - return 0; - } - result = strcmp(methodA->methodName, methodB->methodName); - if (result == 0) - result = strcmp(methodA->signature, methodB->signature); - } - return result; -} - -/* - * This comparison function is called from qsort() to sort - * MethodEntry pointers into alphabetical order of class names. - */ -int compareClassNames(const void *a, const void *b) { - int result; - - const MethodEntry *methodA = *(const MethodEntry**)a; - const MethodEntry *methodB = *(const MethodEntry**)b; - result = strcmp(methodA->className, methodB->className); - if (result == 0) { - int64_t idA = methodA->methodId; - int64_t idB = methodB->methodId; - if (idA < idB) - return -1; - if (idA > idB) - return 1; - return 0; - } - return result; -} - -/* - * This comparison function is called from qsort() to sort - * classes into decreasing order of exclusive elapsed time. - */ -int compareClassExclusive(const void *a, const void *b) { - uint64_t elapsed1, elapsed2; - int result; - - const ClassEntry *classA = *(const ClassEntry**)a; - const ClassEntry *classB = *(const ClassEntry**)b; - elapsed1 = classA->elapsedExclusive; - elapsed2 = classB->elapsedExclusive; - if (elapsed1 < elapsed2) - return 1; - if (elapsed1 > elapsed2) - return -1; - - /* If the elapsed times of two classs are equal, then sort them - * into alphabetical order. - */ - result = strcmp(classA->className, classB->className); - if (result == 0) { - /* Break ties with the first method id. This is probably not - * needed. - */ - int64_t idA = classA->methods[0]->methodId; - int64_t idB = classB->methods[0]->methodId; - if (idA < idB) - return -1; - if (idA > idB) - return 1; - return 0; - } - return result; -} - -/* - * This comparison function is called from qsort() to sort - * MethodEntry pointers into alphabetical order by method name, - * then by class name. - */ -int compareMethodNames(const void *a, const void *b) { - int result; - - const MethodEntry *methodA = *(const MethodEntry**)a; - const MethodEntry *methodB = *(const MethodEntry**)b; - if (methodA->methodName == NULL || methodB->methodName == NULL) { - return compareClassNames(a, b); - } - result = strcmp(methodA->methodName, methodB->methodName); - if (result == 0) { - result = strcmp(methodA->className, methodB->className); - if (result == 0) { - int64_t idA = methodA->methodId; - int64_t idB = methodB->methodId; - if (idA < idB) - return -1; - if (idA > idB) - return 1; - return 0; - } - } - return result; -} - -/* - * This comparison function is called from qsort() to sort - * unique methods into decreasing order of exclusive elapsed time. - */ -int compareUniqueExclusive(const void *a, const void *b) { - uint64_t elapsed1, elapsed2; - int result; - - const UniqueMethodEntry *uniqueA = *(const UniqueMethodEntry**)a; - const UniqueMethodEntry *uniqueB = *(const UniqueMethodEntry**)b; - elapsed1 = uniqueA->elapsedExclusive; - elapsed2 = uniqueB->elapsedExclusive; - if (elapsed1 < elapsed2) - return 1; - if (elapsed1 > elapsed2) - return -1; - - /* If the elapsed times of two methods are equal, then sort them - * into alphabetical order. - */ - result = strcmp(uniqueA->methods[0]->className, - uniqueB->methods[0]->className); - if (result == 0) { - int64_t idA = uniqueA->methods[0]->methodId; - int64_t idB = uniqueB->methods[0]->methodId; - if (idA < idB) - return -1; - if (idA > idB) - return 1; - return 0; - } - return result; -} - -/* - * Free a DataKeys struct. - */ -void freeDataKeys(DataKeys* pKeys) -{ - if (pKeys == NULL) - return; - - free(pKeys->fileData); - free(pKeys->threads); - free(pKeys->methods); - free(pKeys); -} - -/* - * Find the offset to the next occurrence of the specified character. - * - * "data" should point somewhere within the current line. "len" is the - * number of bytes left in the buffer. - * - * Returns -1 if we hit the end of the buffer. - */ -int findNextChar(const char* data, int len, char lookFor) -{ - const char* start = data; - - while (len > 0) { - if (*data == lookFor) - return data - start; - - data++; - len--; - } - - return -1; -} - -/* - * 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 != TOKEN_CHAR) { - next = findNextChar(data, len, '\n'); - if (next < 0) - return -1; - count++; - data += next+1; - len -= next+1; - } - - return count; -} - -/* - * Make sure we're at the start of the right section. - * - * Returns the length of the token line, or -1 if something is wrong. - */ -int checkToken(const char* data, int len, const char* cmpStr) -{ - int cmpLen = strlen(cmpStr); - int next; - - if (*data != TOKEN_CHAR) { - fprintf(stderr, - "ERROR: not at start of %s (found '%.10s')\n", cmpStr, data); - return -1; - } - - next = findNextChar(data, len, '\n'); - if (next < cmpLen+1) - return -1; - - if (strncmp(data+1, cmpStr, cmpLen) != 0) { - fprintf(stderr, "ERROR: '%s' not found (got '%.7s')\n", cmpStr, data+1); - return -1; - } - - return next+1; -} - -/* - * Parse the "*version" section. - */ -long parseVersion(DataKeys* pKeys, long offset, int verbose) -{ - char* data; - char* dataEnd; - int i, count, next; - - if (offset < 0) - return -1; - - data = pKeys->fileData + offset; - dataEnd = pKeys->fileData + pKeys->fileLen; - next = checkToken(data, dataEnd - data, "version"); - if (next <= 0) - return -1; - - data += next; - - /* - * Count the number of items in the "version" section. - */ - count = countLinesToToken(data, dataEnd - data); - if (count <= 0) { - fprintf(stderr, - "ERROR: failed while reading version (found %d)\n", count); - return -1; - } - - /* find the end of the line */ - next = findNextChar(data, dataEnd - data, '\n'); - if (next < 0) - return -1; - - data[next] = '\0'; - versionNumber = strtoul(data, NULL, 0); - if (verbose) - printf("VERSION: %d\n", versionNumber); - - data += next+1; - - /* skip over the rest of the stuff, which is "name=value" lines */ - for (i = 1; i < count; i++) { - next = findNextChar(data, dataEnd - data, '\n'); - if (next < 0) - return -1; - //data[next] = '\0'; - //printf("IGNORING: '%s'\n", data); - data += next+1; - } - - return data - pKeys->fileData; -} - -/* - * Parse the "*threads" section. - */ -long parseThreads(DataKeys* pKeys, long offset) -{ - char* data; - char* dataEnd; - int i, next, tab, count; - - if (offset < 0) - return -1; - - data = pKeys->fileData + offset; - dataEnd = pKeys->fileData + pKeys->fileLen; - next = checkToken(data, dataEnd - data, "threads"); - - data += next; - - /* - * Count the number of thread entries (one per line). - */ - count = countLinesToToken(data, dataEnd - data); - if (count <= 0) { - fprintf(stderr, - "ERROR: failed while reading threads (found %d)\n", count); - return -1; - } - - //printf("+++ found %d threads\n", count); - pKeys->threads = (ThreadEntry*) malloc(sizeof(ThreadEntry) * count); - if (pKeys->threads == NULL) - return -1; - - /* - * Extract all entries. - */ - for (i = 0; i < count; i++) { - next = findNextChar(data, dataEnd - data, '\n'); - assert(next > 0); - data[next] = '\0'; - - tab = findNextChar(data, next, '\t'); - data[tab] = '\0'; - - pKeys->threads[i].threadId = atoi(data); - pKeys->threads[i].threadName = data + tab +1; - - data += next+1; - } - - pKeys->numThreads = count; - return data - pKeys->fileData; -} - -/* - * Parse the "*methods" section. - */ -long parseMethods(DataKeys* pKeys, long offset) -{ - char* data; - char* dataEnd; - int i, next, count; - - if (offset < 0) - return -1; - - data = pKeys->fileData + offset; - dataEnd = pKeys->fileData + pKeys->fileLen; - next = checkToken(data, dataEnd - data, "methods"); - if (next < 0) - return -1; - - data += next; - - /* - * Count the number of method entries (one per line). - */ - count = countLinesToToken(data, dataEnd - data); - if (count <= 0) { - fprintf(stderr, - "ERROR: failed while reading methods (found %d)\n", count); - return -1; - } - - /* Reserve an extra method at location 0 for the "toplevel" method, - * and another extra method for all other "unknown" methods. - */ - count += 2; - pKeys->methods = (MethodEntry*) malloc(sizeof(MethodEntry) * count); - if (pKeys->methods == NULL) - return -1; - initMethodEntry(&pKeys->methods[TOPLEVEL_INDEX], -2, "(toplevel)", - NULL, NULL, NULL, NULL); - initMethodEntry(&pKeys->methods[UNKNOWN_INDEX], -1, "(unknown)", - NULL, NULL, NULL, NULL); - - /* - * Extract all entries, starting with index 2. - */ - for (i = UNKNOWN_INDEX + 1; i < count; i++) { - int tab1, tab2, tab3, tab4, tab5; - int64_t id; - char* endptr; - - next = findNextChar(data, dataEnd - data, '\n'); - assert(next > 0); - data[next] = '\0'; - - tab1 = findNextChar(data, next, '\t'); - tab2 = findNextChar(data+(tab1+1), next-(tab1+1), '\t'); - tab3 = findNextChar(data+(tab1+tab2+2), next-(tab1+tab2+2), '\t'); - tab4 = findNextChar(data+(tab1+tab2+tab3+3), - next-(tab1+tab2+tab3+3), '\t'); - tab5 = findNextChar(data+(tab1+tab2+tab3+tab4+4), - next-(tab1+tab2+tab3+tab4+4), '\t'); - if (tab1 < 0) { - fprintf(stderr, "ERROR: missing field on method line: '%s'\n", - data); - return -1; - } - assert(data[tab1] == '\t'); - data[tab1] = '\0'; - - id = strtoul(data, &endptr, 0); - if (*endptr != '\0') { - fprintf(stderr, "ERROR: bad method ID '%s'\n", data); - return -1; - } - - // Allow files that specify just a function name, instead of requiring - // "class \t method \t signature" - if (tab2 > 0 && tab3 > 0) { - tab2 += tab1+1; - tab3 += tab2+1; - assert(data[tab2] == '\t'); - assert(data[tab3] == '\t'); - data[tab2] = data[tab3] = '\0'; - - // This is starting to get awkward. Allow filename and line #. - if (tab4 > 0 && tab5 > 0) { - tab4 += tab3+1; - tab5 += tab4+1; - - assert(data[tab4] == '\t'); - assert(data[tab5] == '\t'); - data[tab4] = data[tab5] = '\0'; - - initMethodEntry(&pKeys->methods[i], id, data + tab1 +1, - data + tab2 +1, data + tab3 +1, data + tab4 +1, - data + tab5 +1); - } else { - initMethodEntry(&pKeys->methods[i], id, data + tab1 +1, - data + tab2 +1, data + tab3 +1, NULL, NULL); - } - } else { - initMethodEntry(&pKeys->methods[i], id, data + tab1 +1, - NULL, NULL, NULL, NULL); - } - - data += next+1; - } - - pKeys->numMethods = count; - return data - pKeys->fileData; -} - -/* - * Parse the "*end" section. - */ -long parseEnd(DataKeys* pKeys, long offset) -{ - char* data; - char* dataEnd; - int next; - - if (offset < 0) - return -1; - - data = pKeys->fileData + offset; - dataEnd = pKeys->fileData + pKeys->fileLen; - next = checkToken(data, dataEnd - data, "end"); - if (next < 0) - return -1; - - data += next; - - return data - pKeys->fileData; -} - -/* - * Sort the thread list entries. - */ -static int compareThreads(const void* thread1, const void* thread2) -{ - return ((const ThreadEntry*) thread1)->threadId - - ((const ThreadEntry*) thread2)->threadId; -} - -void sortThreadList(DataKeys* pKeys) -{ - qsort(pKeys->threads, pKeys->numThreads, sizeof(pKeys->threads[0]), - compareThreads); -} - -/* - * Sort the method list entries. - */ -static int compareMethods(const void* meth1, const void* meth2) -{ - int64_t id1, id2; - - id1 = ((const MethodEntry*) meth1)->methodId; - id2 = ((const MethodEntry*) meth2)->methodId; - if (id1 < id2) - return -1; - if (id1 > id2) - return 1; - return 0; -} - -void sortMethodList(DataKeys* pKeys) -{ - qsort(pKeys->methods, pKeys->numMethods, sizeof(MethodEntry), - compareMethods); -} - -/* - * Parse the key section, and return a copy of the parsed contents. - */ -DataKeys* parseKeys(FILE *fp, int verbose) -{ - DataKeys* pKeys = NULL; - long offset; - int i; - - pKeys = (DataKeys*) calloc(1, sizeof(DataKeys)); - if (pKeys == NULL) - goto fail; - - /* - * We load the entire file into memory. We do this, rather than memory- - * mapping it, because we want to change some whitespace to NULs. - */ - if (fseek(fp, 0L, SEEK_END) != 0) { - perror("fseek"); - goto fail; - } - pKeys->fileLen = ftell(fp); - if (pKeys->fileLen == 0) { - fprintf(stderr, "Key file is empty.\n"); - goto fail; - } - rewind(fp); - - pKeys->fileData = (char*) malloc(pKeys->fileLen); - if (pKeys->fileData == NULL) { - fprintf(stderr, "ERROR: unable to alloc %ld bytes\n", pKeys->fileLen); - goto fail; - } - - if (fread(pKeys->fileData, 1, pKeys->fileLen, fp) != (size_t) pKeys->fileLen) - { - fprintf(stderr, "ERROR: unable to read %ld bytes from trace file\n", - pKeys->fileLen); - goto fail; - } - - offset = 0; - - offset = parseVersion(pKeys, offset, verbose); - offset = parseThreads(pKeys, offset); - offset = parseMethods(pKeys, offset); - offset = parseEnd(pKeys, offset); - 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); - - sortThreadList(pKeys); - sortMethodList(pKeys); - - /* - * Dump list of threads. - */ - if (verbose) { - printf("Threads (%d):\n", pKeys->numThreads); - for (i = 0; i < pKeys->numThreads; i++) { - printf("%2d %s\n", - pKeys->threads[i].threadId, pKeys->threads[i].threadName); - } - } - -#if 0 - /* - * Dump list of methods. - */ - if (verbose) { - printf("Methods (%d):\n", pKeys->numMethods); - for (i = 0; i < pKeys->numMethods; i++) { - printf("0x%08x %s : %s : %s\n", - pKeys->methods[i].methodId, pKeys->methods[i].className, - pKeys->methods[i].methodName, pKeys->methods[i].signature); - } - } -#endif - - return pKeys; - -fail: - freeDataKeys(pKeys); - return NULL; -} - - -/* - * Read values from the binary data file. - */ - -/* Make the return value "unsigned int" instead of "unsigned short" so that - * we can detect EOF. - */ -unsigned int read2LE(FILE* fp) -{ - unsigned int val; - - val = getc(fp); - val |= getc(fp) << 8; - return val; -} -unsigned int read4LE(FILE* fp) -{ - unsigned int val; - - val = getc(fp); - val |= getc(fp) << 8; - val |= getc(fp) << 16; - val |= getc(fp) << 24; - return val; -} -unsigned long long read8LE(FILE* fp) -{ - unsigned long long val; - - val = getc(fp); - val |= (unsigned long long) getc(fp) << 8; - val |= (unsigned long long) getc(fp) << 16; - val |= (unsigned long long) getc(fp) << 24; - val |= (unsigned long long) getc(fp) << 32; - val |= (unsigned long long) getc(fp) << 40; - val |= (unsigned long long) getc(fp) << 48; - val |= (unsigned long long) getc(fp) << 56; - return val; -} - -/* - * Parse the header of the data section. - * - * Returns with the file positioned at the start of the record data. - */ -int parseDataHeader(FILE *fp, DataHeader* pHeader) -{ - int bytesToRead; - - pHeader->magic = read4LE(fp); - pHeader->version = read2LE(fp); - pHeader->offsetToData = read2LE(fp); - pHeader->startWhen = read8LE(fp); - bytesToRead = pHeader->offsetToData - 16; - if (pHeader->version == 1) { - pHeader->recordSize = 9; - } else if (pHeader->version == 2) { - pHeader->recordSize = 10; - } else if (pHeader->version == 3) { - pHeader->recordSize = read2LE(fp); - bytesToRead -= 2; - } else { - fprintf(stderr, "Unsupported trace file version: %d\n", pHeader->version); - return -1; - } - - if (fseek(fp, bytesToRead, SEEK_CUR) != 0) { - return -1; - } - - return 0; -} - -/* - * Look up a method by it's method ID. - * - * Returns NULL if no matching method was found. - */ -MethodEntry* lookupMethod(DataKeys* pKeys, int64_t methodId) -{ - int hi, lo, mid; - int64_t id; - - lo = 0; - hi = pKeys->numMethods - 1; - - while (hi >= lo) { - mid = (hi + lo) / 2; - - id = pKeys->methods[mid].methodId; - if (id == methodId) /* match */ - return &pKeys->methods[mid]; - else if (id < methodId) /* too low */ - lo = mid + 1; - else /* too high */ - hi = mid - 1; - } - - return NULL; -} - -/* - * Reads the next data record, and assigns the data values to threadId, - * methodVal and elapsedTime. On end-of-file, the threadId, methodVal, - * and elapsedTime are unchanged. Returns 1 on end-of-file, otherwise - * returns 0. - */ -int readDataRecord(FILE *dataFp, DataHeader* dataHeader, - int *threadId, unsigned int *methodVal, uint64_t *elapsedTime) -{ - int id; - int bytesToRead; - - bytesToRead = dataHeader->recordSize; - if (dataHeader->version == 1) { - id = getc(dataFp); - bytesToRead -= 1; - } else { - id = read2LE(dataFp); - bytesToRead -= 2; - } - if (id == EOF) - return 1; - *threadId = id; - - *methodVal = read4LE(dataFp); - *elapsedTime = read4LE(dataFp); - bytesToRead -= 8; - - while (bytesToRead-- > 0) { - getc(dataFp); - } - - if (feof(dataFp)) { - fprintf(stderr, "WARNING: hit EOF mid-record\n"); - return 1; - } - return 0; -} - -/* - * Read the key file and use it to produce formatted output from the - * data file. - */ -void dumpTrace() -{ - static const char* actionStr[] = { "ent", "xit", "unr", "???" }; - MethodEntry bogusMethod = { 0, "???", "???", "???", "???", -1, 0, 0, 0, 0, - {NULL, NULL}, {NULL, NULL}, {0, 0}, 0, 0, -1 }; - char bogusBuf[80]; - char spaces[MAX_STACK_DEPTH+1]; - FILE* dataFp = NULL; - DataHeader dataHeader; - DataKeys* pKeys = NULL; - int i; - TraceData traceData; - - //printf("Dumping '%s' '%s'\n", dataFileName, keyFileName); - - memset(spaces, '.', MAX_STACK_DEPTH); - spaces[MAX_STACK_DEPTH] = '\0'; - - for (i = 0; i < MAX_THREADS; i++) - traceData.depth[i] = 2; // adjust for return from start function - - dataFp = fopen(gOptions.traceFileName, "rb"); - if (dataFp == NULL) - goto bail; - - if ((pKeys = parseKeys(dataFp, 1)) == NULL) - goto bail; - - if (parseDataHeader(dataFp, &dataHeader) < 0) - goto bail; - - printf("Trace (threadID action usecs class.method signature):\n"); - - while (1) { - MethodEntry* method; - int threadId; - unsigned int methodVal; - uint64_t elapsedTime; - int action, printDepth; - int64_t methodId, lastEnter = 0; - int mismatch = 0; - char depthNote; - - /* - * Extract values from file. - */ - if (readDataRecord(dataFp, &dataHeader, &threadId, &methodVal, &elapsedTime)) - break; - - action = METHOD_ACTION(methodVal); - methodId = METHOD_ID(methodVal); - - /* - * Generate a line of output. - */ - if (action == METHOD_TRACE_ENTER) { - traceData.depth[threadId]++; - lastEnter = methodId; - } else { - /* quick test for mismatched adjacent enter/exit */ - if (lastEnter != 0 && lastEnter != methodId) - mismatch = 1; - } - - printDepth = traceData.depth[threadId]; - depthNote = ' '; - if (printDepth < 0) { - printDepth = 0; - depthNote = '-'; - } else if (printDepth > MAX_STACK_DEPTH) { - printDepth = MAX_STACK_DEPTH; - depthNote = '+'; - } - - method = lookupMethod(pKeys, methodId); - if (method == NULL) { - method = &bogusMethod; - sprintf(bogusBuf, "methodId: %#" PRIx64 "", methodId); - method->signature = bogusBuf; - } - - if (method->methodName) { - printf("%2d %s%c %8" PRIu64 "%c%s%s.%s %s\n", threadId, - actionStr[action], mismatch ? '!' : ' ', - elapsedTime, depthNote, - spaces + (MAX_STACK_DEPTH - printDepth), - method->className, method->methodName, method->signature); - } else { - printf("%2d %s%c %8" PRIu64 "%c%s%s\n", threadId, - actionStr[action], mismatch ? '!' : ' ', - elapsedTime, depthNote, - spaces + (MAX_STACK_DEPTH - printDepth), - method->className); - } - - if (action != METHOD_TRACE_ENTER) { - traceData.depth[threadId]--; /* METHOD_TRACE_EXIT or METHOD_TRACE_UNROLL */ - lastEnter = 0; - } - - mismatch = 0; - } - -bail: - if (dataFp != NULL) - fclose(dataFp); - if (pKeys != NULL) - freeDataKeys(pKeys); -} - -/* This routine adds the given time to the parent and child methods. - * This is called when the child routine exits, after the child has - * been popped from the stack. The elapsedTime parameter is the - * duration of the child routine, including time spent in called routines. - */ -void addInclusiveTime(MethodEntry *parent, MethodEntry *child, - uint64_t elapsedTime) -{ - TimedMethod *pTimed; - -#if 0 - bool verbose = false; - if (strcmp(child->className, debugClassName) == 0) - verbose = true; -#endif - - int childIsRecursive = (child->recursiveEntries > 0); - int parentIsRecursive = (parent->recursiveEntries > 1); - - if (child->recursiveEntries == 0) { - child->elapsedInclusive += elapsedTime; - } else if (child->recursiveEntries == 1) { - child->recursiveInclusive += elapsedTime; - } - child->numCalls[childIsRecursive] += 1; - -#if 0 - if (verbose) { - fprintf(stderr, - "%s %d elapsedTime: %lld eI: %lld, rI: %lld\n", - child->className, child->recursiveEntries, - elapsedTime, child->elapsedInclusive, - child->recursiveInclusive); - } -#endif - - /* Find the child method in the parent */ - TimedMethod *children = parent->children[parentIsRecursive]; - for (pTimed = children; pTimed; pTimed = pTimed->next) { - if (pTimed->method == child) { - pTimed->elapsedInclusive += elapsedTime; - pTimed->numCalls += 1; - break; - } - } - if (pTimed == NULL) { - /* Allocate a new TimedMethod */ - pTimed = (TimedMethod *) malloc(sizeof(TimedMethod)); - pTimed->elapsedInclusive = elapsedTime; - pTimed->numCalls = 1; - pTimed->method = child; - - /* Add it to the front of the list */ - pTimed->next = children; - parent->children[parentIsRecursive] = pTimed; - } - - /* Find the parent method in the child */ - TimedMethod *parents = child->parents[childIsRecursive]; - for (pTimed = parents; pTimed; pTimed = pTimed->next) { - if (pTimed->method == parent) { - pTimed->elapsedInclusive += elapsedTime; - pTimed->numCalls += 1; - break; - } - } - if (pTimed == NULL) { - /* Allocate a new TimedMethod */ - pTimed = (TimedMethod *) malloc(sizeof(TimedMethod)); - pTimed->elapsedInclusive = elapsedTime; - pTimed->numCalls = 1; - pTimed->method = parent; - - /* Add it to the front of the list */ - pTimed->next = parents; - child->parents[childIsRecursive] = pTimed; - } - -#if 0 - if (verbose) { - fprintf(stderr, - " %s %d eI: %lld\n", - parent->className, parent->recursiveEntries, - pTimed->elapsedInclusive); - } -#endif -} - -/* Sorts a linked list and returns a newly allocated array containing - * the sorted entries. - */ -TimedMethod *sortTimedMethodList(TimedMethod *list, int *num) -{ - int ii; - TimedMethod *pTimed, *sorted; - - /* Count the elements */ - int num_entries = 0; - for (pTimed = list; pTimed; pTimed = pTimed->next) - num_entries += 1; - *num = num_entries; - if (num_entries == 0) - return NULL; - - /* Copy all the list elements to a new array and sort them */ - sorted = (TimedMethod *) malloc(sizeof(TimedMethod) * num_entries); - for (ii = 0, pTimed = list; pTimed; pTimed = pTimed->next, ++ii) - memcpy(&sorted[ii], pTimed, sizeof(TimedMethod)); - qsort(sorted, num_entries, sizeof(TimedMethod), compareTimedMethod); - - /* Fix up the "next" pointers so that they work. */ - for (ii = 0; ii < num_entries - 1; ++ii) - sorted[ii].next = &sorted[ii + 1]; - sorted[num_entries - 1].next = NULL; - - return sorted; -} - -/* Define flag values for printInclusiveMethod() */ -static const int kIsRecursive = 1; - -/* This prints the inclusive stats for all the parents or children of a - * method, depending on the list that is passed in. - */ -void printInclusiveMethod(MethodEntry *method, TimedMethod *list, int numCalls, - int flags) -{ - int num; - TimedMethod *pTimed; - char buf[80]; - char *anchor_close; - char *spaces = " "; /* 6 spaces */ - int num_spaces = strlen(spaces); - char *space_ptr = &spaces[num_spaces]; - char *className, *methodName, *signature; - char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE]; - char signatureBuf[HTML_BUFSIZE]; - - anchor_close = ""; - if (gOptions.outputHtml) - anchor_close = "</a>"; - - TimedMethod *sorted = sortTimedMethodList(list, &num); - double methodTotal = method->elapsedInclusive; - for (pTimed = sorted; pTimed; pTimed = pTimed->next) { - MethodEntry *relative = pTimed->method; - className = (char*)(relative->className); - methodName = (char*)(relative->methodName); - signature = (char*)(relative->signature); - double per = 100.0 * pTimed->elapsedInclusive / methodTotal; - sprintf(buf, "[%d]", relative->index); - if (gOptions.outputHtml) { - int len = strlen(buf); - if (len > num_spaces) - len = num_spaces; - sprintf(buf, "<a href=\"#m%d\">[%d]", - relative->index, relative->index); - space_ptr = &spaces[len]; - className = htmlEscape(className, classBuf, HTML_BUFSIZE); - methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE); - signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE); - } - int nCalls = numCalls; - if (nCalls == 0) - nCalls = relative->numCalls[0] + relative->numCalls[1]; - if (relative->methodName) { - if (flags & kIsRecursive) { - // Don't display percentages for recursive functions - printf("%6s %5s %6s %s%6s%s %6d/%-6d %9" PRIu64 " %s.%s %s\n", - "", "", "", - space_ptr, buf, anchor_close, - pTimed->numCalls, nCalls, - pTimed->elapsedInclusive, - className, methodName, signature); - } else { - printf("%6s %5s %5.1f%% %s%6s%s %6d/%-6d %9" PRIu64 - " %s.%s %s\n", - "", "", per, - space_ptr, buf, anchor_close, - pTimed->numCalls, nCalls, - pTimed->elapsedInclusive, - className, methodName, signature); - } - } else { - if (flags & kIsRecursive) { - // Don't display percentages for recursive functions - printf("%6s %5s %6s %s%6s%s %6d/%-6d %9" PRIu64 " %s\n", - "", "", "", - space_ptr, buf, anchor_close, - pTimed->numCalls, nCalls, - pTimed->elapsedInclusive, - className); - } else { - printf("%6s %5s %5.1f%% %s%6s%s %6d/%-6d %9" PRIu64 " %s\n", - "", "", per, - space_ptr, buf, anchor_close, - pTimed->numCalls, nCalls, - pTimed->elapsedInclusive, - className); - } - } - } -} - -void countRecursiveEntries(CallStack *pStack, int top, MethodEntry *method) -{ - int ii; - - method->recursiveEntries = 0; - for (ii = 0; ii < top; ++ii) { - if (pStack->calls[ii].method == method) - method->recursiveEntries += 1; - } -} - -void stackDump(CallStack *pStack, int top) -{ - int ii; - - for (ii = 0; ii < top; ++ii) { - MethodEntry *method = pStack->calls[ii].method; - uint64_t entryTime = pStack->calls[ii].entryTime; - if (method->methodName) { - fprintf(stderr, " %2d: %8" PRIu64 " %s.%s %s\n", ii, entryTime, - method->className, method->methodName, method->signature); - } else { - fprintf(stderr, " %2d: %8" PRIu64 " %s\n", ii, entryTime, - method->className); - } - } -} - -void outputTableOfContents() -{ - printf("<a name=\"contents\"></a>\n"); - printf("<h2>Table of Contents</h2>\n"); - 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=\"#class\">Class/method profile</a></li>\n"); - printf(" <li><a href=\"#method\">Method/class profile</a></li>\n"); - printf("</ul>\n\n"); -} - -void outputNavigationBar() -{ - printf("<a href=\"#contents\">[Top]</a>\n"); - printf("<a href=\"#exclusive\">[Exclusive]</a>\n"); - printf("<a href=\"#inclusive\">[Inclusive]</a>\n"); - printf("<a href=\"#class\">[Class]</a>\n"); - printf("<a href=\"#method\">[Method]</a>\n"); - printf("<br><br>\n"); -} - -void printExclusiveProfile(MethodEntry **pMethods, int numMethods, - uint64_t sumThreadTime) -{ - int ii; - MethodEntry* method; - double total, sum, per, sum_per; - 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=\"exclusive\"></a>\n"); - printf("<hr>\n"); - outputNavigationBar(); - } else { - printf("\n%s\n", profileSeparator); - } - - /* First, sort the methods into decreasing order of inclusive - * elapsed time so that we can assign the method indices. - */ - qsort(pMethods, numMethods, sizeof(MethodEntry*), compareElapsedInclusive); - - for (ii = 0; ii < numMethods; ++ii) - pMethods[ii]->index = ii; - - /* Sort the methods into decreasing order of exclusive elapsed time. - */ - qsort(pMethods, numMethods, sizeof(MethodEntry*), - compareElapsedExclusive); - - printf("Total cycles: %" PRIu64 "\n\n", sumThreadTime); - if (gOptions.outputHtml) { - printf("<br><br>\n"); - } - printf("Exclusive elapsed times for each method, not including time spent in\n"); - printf("children, sorted by exclusive time.\n\n"); - if (gOptions.outputHtml) { - printf("<br><br>\n<pre>\n"); - } - - printf(" Usecs self %% sum %% Method\n"); - sum = 0; - - for (ii = 0; ii < numMethods; ++ii) { - char *className, *methodName, *signature; - - method = pMethods[ii]; - /* Don't show methods with zero cycles */ - if (method->elapsedExclusive == 0) - break; - className = (char*)(method->className); - methodName = (char*)(method->methodName); - signature = (char*)(method->signature); - sum += method->elapsedExclusive; - per = 100.0 * method->elapsedExclusive / total; - sum_per = 100.0 * sum / total; - if (gOptions.outputHtml) { - sprintf(anchor_buf, "<a href=\"#m%d\">", method->index); - className = htmlEscape(className, classBuf, HTML_BUFSIZE); - methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE); - signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE); - } - if (method->methodName) { - printf("%9" PRIu64 " %6.2f %6.2f %s[%d]%s %s.%s %s\n", - method->elapsedExclusive, per, sum_per, - anchor_buf, method->index, anchor_close, - className, methodName, signature); - } else { - printf("%9" PRIu64 " %6.2f %6.2f %s[%d]%s %s\n", - method->elapsedExclusive, per, sum_per, - anchor_buf, method->index, anchor_close, - className); - } - } - if (gOptions.outputHtml) { - printf("</pre>\n"); - } -} - -/* check to make sure that the child method meets the threshold of the parent */ -int checkThreshold(MethodEntry* parent, MethodEntry* child) -{ - double parentTime = parent->elapsedInclusive; - double childTime = child->elapsedInclusive; - int64_t percentage = (childTime / parentTime) * 100.0; - return (percentage < gOptions.threshold) ? 0 : 1; -} - -void createLabels(FILE* file, MethodEntry* method) -{ - fprintf(file, "node%d[label = \"[%d] %s.%s (%" PRIu64 ", %" PRIu64 ", %d)\"]\n", - method->index, method->index, method->className, method->methodName, - method->elapsedInclusive / 1000, - method->elapsedExclusive / 1000, - method->numCalls[0]); - - method->graphState = GRAPH_LABEL_VISITED; - - TimedMethod* child; - for (child = method->children[0] ; child ; child = child->next) { - MethodEntry* childMethod = child->method; - - if ((childMethod->graphState & GRAPH_LABEL_VISITED) == 0 && checkThreshold(method, childMethod)) { - createLabels(file, child->method); - } - } -} - -void createLinks(FILE* file, MethodEntry* method) -{ - method->graphState |= GRAPH_NODE_VISITED; - - TimedMethod* child; - for (child = method->children[0] ; child ; child = child->next) { - MethodEntry* childMethod = child->method; - if (checkThreshold(method, child->method)) { - fprintf(file, "node%d -> node%d\n", method->index, child->method->index); - // only visit children that haven't been visited before - if ((childMethod->graphState & GRAPH_NODE_VISITED) == 0) { - createLinks(file, child->method); - } - } - } -} - -void createInclusiveProfileGraphNew(DataKeys* dataKeys) -{ - // create a temporary file in /tmp - char path[FILENAME_MAX]; - if (gOptions.keepDotFile) { - snprintf(path, FILENAME_MAX, "%s.dot", gOptions.graphFileName); - } else { - snprintf(path, FILENAME_MAX, "dot-%d-%d.dot", (int)time(NULL), rand()); - } - - FILE* file = fopen(path, "w+"); - - fprintf(file, "digraph g {\nnode [shape = record,height=.1];\n"); - - createLabels(file, dataKeys->methods); - createLinks(file, dataKeys->methods); - - fprintf(file, "}"); - fclose(file); - - // now that we have the dot file generate the image - char command[1024]; - snprintf(command, 1024, "dot -Tpng -o \"%s\" \"%s\"", gOptions.graphFileName, path); - - system(command); - - if (! gOptions.keepDotFile) { - remove(path); - } -} - -void printInclusiveProfile(MethodEntry **pMethods, int numMethods, - uint64_t sumThreadTime) -{ - int ii; - MethodEntry* method; - double total, per; - 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(); - } else { - printf("\n%s\n", profileSeparator); - } - - /* Sort the methods into decreasing order of inclusive elapsed time. */ - qsort(pMethods, numMethods, sizeof(MethodEntry*), - compareElapsedInclusive); - - printf("\nInclusive elapsed times for each method and its parents and children,\n"); - printf("sorted by inclusive time.\n\n"); - - if (gOptions.outputHtml) { - printf("<br><br>\n<pre>\n"); - } - - printf("index %%/total %%/self index calls usecs name\n"); - for (ii = 0; ii < numMethods; ++ii) { - double excl_per; - char buf[40]; - char *className, *methodName, *signature; - - method = pMethods[ii]; - /* Don't show methods with zero cycles */ - if (method->elapsedInclusive == 0) - break; - - className = (char*)(method->className); - methodName = (char*)(method->methodName); - signature = (char*)(method->signature); - - if (gOptions.outputHtml) { - printf("<a name=\"m%d\"></a>", method->index); - className = htmlEscape(className, classBuf, HTML_BUFSIZE); - methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE); - signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE); - } - printf("----------------------------------------------------\n"); - - /* Sort and print the parents */ - int numCalls = method->numCalls[0] + method->numCalls[1]; - printInclusiveMethod(method, method->parents[0], numCalls, 0); - if (method->parents[1]) { - printf(" +++++++++++++++++++++++++\n"); - printInclusiveMethod(method, method->parents[1], numCalls, - kIsRecursive); - } - - per = 100.0 * method->elapsedInclusive / total; - sprintf(buf, "[%d]", ii); - if (method->methodName) { - printf("%-6s %5.1f%% %5s %6s %6d+%-6d %9" PRIu64 " %s.%s %s\n", - buf, - per, "", "", method->numCalls[0], method->numCalls[1], - method->elapsedInclusive, - className, methodName, signature); - } else { - printf("%-6s %5.1f%% %5s %6s %6d+%-6d %9" PRIu64 " %s\n", - buf, - per, "", "", method->numCalls[0], method->numCalls[1], - method->elapsedInclusive, - className); - } - excl_per = 100.0 * method->topExclusive / method->elapsedInclusive; - printf("%6s %5s %5.1f%% %6s %6s %6s %9" PRIu64 "\n", - "", "", excl_per, "excl", "", "", method->topExclusive); - - /* Sort and print the children */ - printInclusiveMethod(method, method->children[0], 0, 0); - if (method->children[1]) { - printf(" +++++++++++++++++++++++++\n"); - printInclusiveMethod(method, method->children[1], 0, - kIsRecursive); - } - } - if (gOptions.outputHtml) { - printf("</pre>\n"); - } -} - -void createClassList(TraceData* traceData, MethodEntry **pMethods, int numMethods) -{ - int ii; - - /* Sort the methods into alphabetical order to find the unique class - * names. - */ - qsort(pMethods, numMethods, sizeof(MethodEntry*), compareClassNames); - - /* Count the number of unique class names. */ - const char *currentClassName = ""; - const char *firstClassName = NULL; - traceData->numClasses = 0; - for (ii = 0; ii < numMethods; ++ii) { - if (pMethods[ii]->methodName == NULL) { - continue; - } - if (strcmp(pMethods[ii]->className, currentClassName) != 0) { - // Remember the first one - if (firstClassName == NULL) { - firstClassName = pMethods[ii]->className; - } - traceData->numClasses += 1; - currentClassName = pMethods[ii]->className; - } - } - - if (traceData->numClasses == 0) { - traceData->classes = NULL; - return; - } - - /* Allocate space for all of the unique class names */ - traceData->classes = (ClassEntry *) malloc(sizeof(ClassEntry) * traceData->numClasses); - - /* Initialize the classes array */ - memset(traceData->classes, 0, sizeof(ClassEntry) * traceData->numClasses); - ClassEntry *pClass = traceData->classes; - pClass->className = currentClassName = firstClassName; - int prevNumMethods = 0; - for (ii = 0; ii < numMethods; ++ii) { - if (pMethods[ii]->methodName == NULL) { - continue; - } - if (strcmp(pMethods[ii]->className, currentClassName) != 0) { - pClass->numMethods = prevNumMethods; - (++pClass)->className = currentClassName = pMethods[ii]->className; - prevNumMethods = 0; - } - prevNumMethods += 1; - } - pClass->numMethods = prevNumMethods; - - /* Create the array of MethodEntry pointers for each class */ - pClass = NULL; - currentClassName = ""; - int nextMethod = 0; - for (ii = 0; ii < numMethods; ++ii) { - if (pMethods[ii]->methodName == NULL) { - continue; - } - if (strcmp(pMethods[ii]->className, currentClassName) != 0) { - currentClassName = pMethods[ii]->className; - if (pClass == NULL) - pClass = traceData->classes; - else - pClass++; - /* Allocate space for the methods array */ - int nbytes = sizeof(MethodEntry*) * pClass->numMethods; - pClass->methods = (MethodEntry**) malloc(nbytes); - nextMethod = 0; - } - pClass->methods[nextMethod++] = pMethods[ii]; - } -} - -/* Prints a number of html non-breaking spaces according so that the length - * of the string "buf" is at least "width" characters wide. If width is - * negative, then trailing spaces are added instead of leading spaces. - */ -void printHtmlField(char *buf, int width) -{ - int ii; - - int leadingSpaces = 1; - if (width < 0) { - width = -width; - leadingSpaces = 0; - } - int len = strlen(buf); - int numSpaces = width - len; - if (numSpaces <= 0) { - printf("%s", buf); - return; - } - if (leadingSpaces == 0) - printf("%s", buf); - for (ii = 0; ii < numSpaces; ++ii) - printf(" "); - if (leadingSpaces == 1) - printf("%s", buf); -} - -void printClassProfiles(TraceData* traceData, uint64_t sumThreadTime) -{ - int ii, jj; - MethodEntry* method; - double total, sum, per, sum_per; - char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE]; - char signatureBuf[HTML_BUFSIZE]; - - total = sumThreadTime; - if (gOptions.outputHtml) { - printf("<a name=\"class\"></a>\n"); - printf("<hr>\n"); - outputNavigationBar(); - } else { - printf("\n%s\n", profileSeparator); - } - - if (traceData->numClasses == 0) { - printf("\nNo classes.\n"); - if (gOptions.outputHtml) { - printf("<br><br>\n"); - } - return; - } - - printf("\nExclusive elapsed time for each class, summed over all the methods\n"); - printf("in the class.\n\n"); - if (gOptions.outputHtml) { - printf("<br><br>\n"); - } - - /* For each class, sum the exclusive times in all of the methods - * in that class. Also sum the number of method calls. Also - * sort the methods so the most expensive appear at the top. - */ - ClassEntry *pClass = traceData->classes; - for (ii = 0; ii < traceData->numClasses; ++ii, ++pClass) { - //printf("%s %d methods\n", pClass->className, pClass->numMethods); - int numMethods = pClass->numMethods; - for (jj = 0; jj < numMethods; ++jj) { - method = pClass->methods[jj]; - pClass->elapsedExclusive += method->elapsedExclusive; - pClass->numCalls[0] += method->numCalls[0]; - pClass->numCalls[1] += method->numCalls[1]; - } - - /* Sort the methods into decreasing order of exclusive time */ - qsort(pClass->methods, numMethods, sizeof(MethodEntry*), - compareElapsedExclusive); - } - - /* Allocate an array of pointers to the classes for more efficient - * sorting. - */ - ClassEntry **pClasses; - pClasses = (ClassEntry**) malloc(sizeof(ClassEntry*) * traceData->numClasses); - for (ii = 0; ii < traceData->numClasses; ++ii) - pClasses[ii] = &traceData->classes[ii]; - - /* Sort the classes into decreasing order of exclusive time */ - qsort(pClasses, traceData->numClasses, sizeof(ClassEntry*), compareClassExclusive); - - if (gOptions.outputHtml) { - printf("<div class=\"header\"><span class=\"parent\"> </span> "); - printf("Cycles %%/total Cumul.%% Calls+Recur Class</div>\n"); - } else { - printf(" Cycles %%/total Cumul.%% Calls+Recur Class\n"); - } - - sum = 0; - for (ii = 0; ii < traceData->numClasses; ++ii) { - char *className, *methodName, *signature; - - /* Skip classes with zero cycles */ - pClass = pClasses[ii]; - if (pClass->elapsedExclusive == 0) - break; - - per = 100.0 * pClass->elapsedExclusive / total; - sum += pClass->elapsedExclusive; - sum_per = 100.0 * sum / total; - className = (char*)(pClass->className); - if (gOptions.outputHtml) { - char buf[80]; - - className = htmlEscape(className, classBuf, HTML_BUFSIZE); - printf("<div class=\"link\" onClick=\"javascript:toggle('d%d')\" onMouseOver=\"javascript:onMouseOver(this)\" onMouseOut=\"javascript:onMouseOut(this)\"><span class=\"parent\" id=\"xd%d\">+</span>", ii, ii); - sprintf(buf, "%" PRIu64, pClass->elapsedExclusive); - printHtmlField(buf, 9); - printf(" "); - sprintf(buf, "%.1f", per); - printHtmlField(buf, 7); - printf(" "); - sprintf(buf, "%.1f", sum_per); - printHtmlField(buf, 7); - printf(" "); - sprintf(buf, "%d", pClass->numCalls[0]); - printHtmlField(buf, 6); - printf("+"); - sprintf(buf, "%d", pClass->numCalls[1]); - printHtmlField(buf, -6); - printf(" "); - printf("%s", className); - printf("</div>\n"); - printf("<div class=\"parent\" id=\"d%d\">\n", ii); - } else { - printf("---------------------------------------------\n"); - printf("%9" PRIu64 " %7.1f %7.1f %6d+%-6d %s\n", - pClass->elapsedExclusive, per, sum_per, - pClass->numCalls[0], pClass->numCalls[1], - className); - } - - int numMethods = pClass->numMethods; - double classExclusive = pClass->elapsedExclusive; - double sumMethods = 0; - for (jj = 0; jj < numMethods; ++jj) { - method = pClass->methods[jj]; - methodName = (char*)(method->methodName); - signature = (char*)(method->signature); - per = 100.0 * method->elapsedExclusive / classExclusive; - sumMethods += method->elapsedExclusive; - sum_per = 100.0 * sumMethods / classExclusive; - if (gOptions.outputHtml) { - char buf[80]; - - methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE); - signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE); - printf("<div class=\"leaf\"><span class=\"leaf\"> </span>"); - sprintf(buf, "%" PRIu64, method->elapsedExclusive); - printHtmlField(buf, 9); - printf(" "); - sprintf(buf, "%" PRIu64, method->elapsedInclusive); - printHtmlField(buf, 9); - printf(" "); - sprintf(buf, "%.1f", per); - printHtmlField(buf, 7); - printf(" "); - sprintf(buf, "%.1f", sum_per); - printHtmlField(buf, 7); - printf(" "); - sprintf(buf, "%d", method->numCalls[0]); - printHtmlField(buf, 6); - printf("+"); - sprintf(buf, "%d", method->numCalls[1]); - printHtmlField(buf, -6); - printf(" "); - printf("<a href=\"#m%d\">[%d]</a> %s %s", - method->index, method->index, methodName, signature); - printf("</div>\n"); - } else { - printf("%9" PRIu64 " %9" PRIu64 " %7.1f %7.1f %6d+%-6d [%d] %s %s\n", - method->elapsedExclusive, - method->elapsedInclusive, - per, sum_per, - method->numCalls[0], method->numCalls[1], - method->index, methodName, signature); - } - } - if (gOptions.outputHtml) { - printf("</div>\n"); - } - } -} - -void createUniqueMethodList(TraceData* traceData, MethodEntry **pMethods, int numMethods) -{ - int ii; - - /* Sort the methods into alphabetical order of method names - * to find the unique method names. - */ - qsort(pMethods, numMethods, sizeof(MethodEntry*), compareMethodNames); - - /* Count the number of unique method names, ignoring class and - * signature. - */ - const char *currentMethodName = ""; - traceData->numUniqueMethods = 0; - for (ii = 0; ii < numMethods; ++ii) { - if (pMethods[ii]->methodName == NULL) - continue; - if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) { - traceData->numUniqueMethods += 1; - currentMethodName = pMethods[ii]->methodName; - } - } - if (traceData->numUniqueMethods == 0) - return; - - /* Allocate space for pointers to all of the unique methods */ - int nbytes = sizeof(UniqueMethodEntry) * traceData->numUniqueMethods; - traceData->uniqueMethods = (UniqueMethodEntry *) malloc(nbytes); - - /* Initialize the uniqueMethods array */ - memset(traceData->uniqueMethods, 0, nbytes); - UniqueMethodEntry *pUnique = traceData->uniqueMethods; - currentMethodName = NULL; - int prevNumMethods = 0; - for (ii = 0; ii < numMethods; ++ii) { - if (pMethods[ii]->methodName == NULL) - continue; - if (currentMethodName == NULL) - currentMethodName = pMethods[ii]->methodName; - if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) { - currentMethodName = pMethods[ii]->methodName; - pUnique->numMethods = prevNumMethods; - pUnique++; - prevNumMethods = 0; - } - prevNumMethods += 1; - } - pUnique->numMethods = prevNumMethods; - - /* Create the array of MethodEntry pointers for each unique method */ - pUnique = NULL; - currentMethodName = ""; - int nextMethod = 0; - for (ii = 0; ii < numMethods; ++ii) { - if (pMethods[ii]->methodName == NULL) - continue; - if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) { - currentMethodName = pMethods[ii]->methodName; - if (pUnique == NULL) - pUnique = traceData->uniqueMethods; - else - pUnique++; - /* Allocate space for the methods array */ - int nbytes = sizeof(MethodEntry*) * pUnique->numMethods; - pUnique->methods = (MethodEntry**) malloc(nbytes); - nextMethod = 0; - } - pUnique->methods[nextMethod++] = pMethods[ii]; - } -} - -void printMethodProfiles(TraceData* traceData, uint64_t sumThreadTime) -{ - int ii, jj; - MethodEntry* method; - double total, sum, per, sum_per; - char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE]; - char signatureBuf[HTML_BUFSIZE]; - - if (traceData->numUniqueMethods == 0) - return; - - total = sumThreadTime; - if (gOptions.outputHtml) { - printf("<a name=\"method\"></a>\n"); - printf("<hr>\n"); - outputNavigationBar(); - } else { - printf("\n%s\n", profileSeparator); - } - - printf("\nExclusive elapsed time for each method, summed over all the classes\n"); - printf("that contain a method with the same name.\n\n"); - if (gOptions.outputHtml) { - printf("<br><br>\n"); - } - - /* For each unique method, sum the exclusive times in all of the methods - * with the same name. Also sum the number of method calls. Also - * sort the methods so the most expensive appear at the top. - */ - UniqueMethodEntry *pUnique = traceData->uniqueMethods; - for (ii = 0; ii < traceData->numUniqueMethods; ++ii, ++pUnique) { - int numMethods = pUnique->numMethods; - for (jj = 0; jj < numMethods; ++jj) { - method = pUnique->methods[jj]; - pUnique->elapsedExclusive += method->elapsedExclusive; - pUnique->numCalls[0] += method->numCalls[0]; - pUnique->numCalls[1] += method->numCalls[1]; - } - - /* Sort the methods into decreasing order of exclusive time */ - qsort(pUnique->methods, numMethods, sizeof(MethodEntry*), - compareElapsedExclusive); - } - - /* Allocate an array of pointers to the methods for more efficient - * sorting. - */ - UniqueMethodEntry **pUniqueMethods; - int nbytes = sizeof(UniqueMethodEntry*) * traceData->numUniqueMethods; - pUniqueMethods = (UniqueMethodEntry**) malloc(nbytes); - for (ii = 0; ii < traceData->numUniqueMethods; ++ii) - pUniqueMethods[ii] = &traceData->uniqueMethods[ii]; - - /* Sort the methods into decreasing order of exclusive time */ - qsort(pUniqueMethods, traceData->numUniqueMethods, sizeof(UniqueMethodEntry*), - compareUniqueExclusive); - - if (gOptions.outputHtml) { - printf("<div class=\"header\"><span class=\"parent\"> </span> "); - printf("Cycles %%/total Cumul.%% Calls+Recur Method</div>\n"); - } else { - printf(" Cycles %%/total Cumul.%% Calls+Recur Method\n"); - } - - sum = 0; - for (ii = 0; ii < traceData->numUniqueMethods; ++ii) { - char *className, *methodName, *signature; - - /* Skip methods with zero cycles */ - pUnique = pUniqueMethods[ii]; - if (pUnique->elapsedExclusive == 0) - break; - - per = 100.0 * pUnique->elapsedExclusive / total; - sum += pUnique->elapsedExclusive; - sum_per = 100.0 * sum / total; - methodName = (char*)(pUnique->methods[0]->methodName); - if (gOptions.outputHtml) { - char buf[80]; - - methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE); - printf("<div class=\"link\" onClick=\"javascript:toggle('e%d')\" onMouseOver=\"javascript:onMouseOver(this)\" onMouseOut=\"javascript:onMouseOut(this)\"><span class=\"parent\" id=\"xe%d\">+</span>", ii, ii); - sprintf(buf, "%" PRIu64, pUnique->elapsedExclusive); - printHtmlField(buf, 9); - printf(" "); - sprintf(buf, "%.1f", per); - printHtmlField(buf, 7); - printf(" "); - sprintf(buf, "%.1f", sum_per); - printHtmlField(buf, 7); - printf(" "); - sprintf(buf, "%d", pUnique->numCalls[0]); - printHtmlField(buf, 6); - printf("+"); - sprintf(buf, "%d", pUnique->numCalls[1]); - printHtmlField(buf, -6); - printf(" "); - printf("%s", methodName); - printf("</div>\n"); - printf("<div class=\"parent\" id=\"e%d\">\n", ii); - } else { - printf("---------------------------------------------\n"); - printf("%9" PRIu64 " %7.1f %7.1f %6d+%-6d %s\n", - pUnique->elapsedExclusive, per, sum_per, - pUnique->numCalls[0], pUnique->numCalls[1], - methodName); - } - int numMethods = pUnique->numMethods; - double methodExclusive = pUnique->elapsedExclusive; - double sumMethods = 0; - for (jj = 0; jj < numMethods; ++jj) { - method = pUnique->methods[jj]; - className = (char*)(method->className); - signature = (char*)(method->signature); - per = 100.0 * method->elapsedExclusive / methodExclusive; - sumMethods += method->elapsedExclusive; - sum_per = 100.0 * sumMethods / methodExclusive; - if (gOptions.outputHtml) { - char buf[80]; - - className = htmlEscape(className, classBuf, HTML_BUFSIZE); - signature = htmlEscape(signature, signatureBuf, HTML_BUFSIZE); - printf("<div class=\"leaf\"><span class=\"leaf\"> </span>"); - sprintf(buf, "%" PRIu64, method->elapsedExclusive); - printHtmlField(buf, 9); - printf(" "); - sprintf(buf, "%" PRIu64, method->elapsedInclusive); - printHtmlField(buf, 9); - printf(" "); - sprintf(buf, "%.1f", per); - printHtmlField(buf, 7); - printf(" "); - sprintf(buf, "%.1f", sum_per); - printHtmlField(buf, 7); - printf(" "); - sprintf(buf, "%d", method->numCalls[0]); - printHtmlField(buf, 6); - printf("+"); - sprintf(buf, "%d", method->numCalls[1]); - printHtmlField(buf, -6); - printf(" "); - printf("<a href=\"#m%d\">[%d]</a> %s.%s %s", - method->index, method->index, - className, methodName, signature); - printf("</div>\n"); - } else { - printf("%9" PRIu64 " %9" PRIu64 " %7.1f %7.1f %6d+%-6d [%d] %s.%s %s\n", - method->elapsedExclusive, - method->elapsedInclusive, - per, sum_per, - method->numCalls[0], method->numCalls[1], - method->index, className, methodName, signature); - } - } - if (gOptions.outputHtml) { - printf("</div>\n"); - } - } -} - -/* - * Read the key and data files and return the MethodEntries for those files - */ -DataKeys* parseDataKeys(TraceData* traceData, const char* traceFileName, uint64_t* threadTime) -{ - DataKeys* dataKeys = NULL; - MethodEntry* method; - FILE* dataFp = NULL; - DataHeader dataHeader; - int ii; - uint64_t currentTime; - MethodEntry* caller; - - dataFp = fopen(traceFileName, "rb"); - if (dataFp == NULL) - goto bail; - - if ((dataKeys = parseKeys(dataFp, 0)) == NULL) - goto bail; - - if (parseDataHeader(dataFp, &dataHeader) < 0) - goto bail; - -#if 0 - FILE *dumpStream = fopen("debug", "w"); -#endif - while (1) { - int threadId; - unsigned int methodVal; - int action; - int64_t methodId; - CallStack *pStack; - /* - * Extract values from file. - */ - if (readDataRecord(dataFp, &dataHeader, &threadId, &methodVal, ¤tTime)) - break; - - action = METHOD_ACTION(methodVal); - methodId = METHOD_ID(methodVal); - - /* Get the call stack for this thread */ - pStack = traceData->stacks[threadId]; - - /* If there is no call stack yet for this thread, then allocate one */ - if (pStack == NULL) { - pStack = malloc(sizeof(CallStack)); - pStack->top = 0; - pStack->lastEventTime = currentTime; - pStack->threadStartTime = currentTime; - traceData->stacks[threadId] = pStack; - } - - /* Lookup the current method */ - method = lookupMethod(dataKeys, methodId); - if (method == NULL) - method = &dataKeys->methods[UNKNOWN_INDEX]; - -#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); - } else { - fprintf(dumpStream, "%2d %-8llu %d %8llu r %d c %d %s\n", - threadId, currentTime, action, pStack->threadStartTime, - method->recursiveEntries, - pStack->top, method->className); - } -#endif - - if (action == METHOD_TRACE_ENTER) { - /* This is a method entry */ - if (pStack->top >= MAX_STACK_DEPTH) { - fprintf(stderr, "Stack overflow (exceeded %d frames)\n", - MAX_STACK_DEPTH); - exit(1); - } - - /* Get the caller method */ - if (pStack->top >= 1) - caller = pStack->calls[pStack->top - 1].method; - else - caller = &dataKeys->methods[TOPLEVEL_INDEX]; - countRecursiveEntries(pStack, pStack->top, caller); - caller->elapsedExclusive += currentTime - pStack->lastEventTime; -#if 0 - if (caller->elapsedExclusive > 10000000) - fprintf(dumpStream, "%llu current %llu last %llu diff %llu\n", - caller->elapsedExclusive, currentTime, - pStack->lastEventTime, - currentTime - pStack->lastEventTime); -#endif - if (caller->recursiveEntries <= 1) { - caller->topExclusive += currentTime - pStack->lastEventTime; - } - - /* Push the method on the stack for this thread */ - pStack->calls[pStack->top].method = method; - pStack->calls[pStack->top++].entryTime = currentTime; - } else { - /* This is a method exit */ - uint64_t entryTime = 0; - - /* Pop the method off the stack for this thread */ - if (pStack->top > 0) { - pStack->top -= 1; - entryTime = pStack->calls[pStack->top].entryTime; - if (method != pStack->calls[pStack->top].method) { - if (method->methodName) { - fprintf(stderr, - "Exit from method %s.%s %s does not match stack:\n", - method->className, method->methodName, - method->signature); - } else { - fprintf(stderr, - "Exit from method %s does not match stack:\n", - method->className); - } - stackDump(pStack, pStack->top + 1); - exit(1); - } - } - - /* Get the caller method */ - if (pStack->top >= 1) - caller = pStack->calls[pStack->top - 1].method; - else - caller = &dataKeys->methods[TOPLEVEL_INDEX]; - countRecursiveEntries(pStack, pStack->top, caller); - countRecursiveEntries(pStack, pStack->top, method); - uint64_t elapsed = currentTime - entryTime; - addInclusiveTime(caller, method, elapsed); - method->elapsedExclusive += currentTime - pStack->lastEventTime; - if (method->recursiveEntries == 0) { - method->topExclusive += currentTime - pStack->lastEventTime; - } - } - /* Remember the time of the last entry or exit event */ - pStack->lastEventTime = currentTime; - } - - /* If we have calls on the stack when the trace ends, then clean - * up the stack and add time to the callers by pretending that we - * are exiting from their methods now. - */ - CallStack *pStack; - int threadId; - uint64_t sumThreadTime = 0; - for (threadId = 0; threadId < MAX_THREADS; ++threadId) { - pStack = traceData->stacks[threadId]; - - /* If this thread never existed, then continue with next thread */ - if (pStack == NULL) - continue; - - /* Also, add up the time taken by all of the threads */ - sumThreadTime += pStack->lastEventTime - pStack->threadStartTime; - - for (ii = 0; ii < pStack->top; ++ii) { - if (ii == 0) - caller = &dataKeys->methods[TOPLEVEL_INDEX]; - else - caller = pStack->calls[ii - 1].method; - method = pStack->calls[ii].method; - countRecursiveEntries(pStack, ii, caller); - countRecursiveEntries(pStack, ii, method); - - uint64_t entryTime = pStack->calls[ii].entryTime; - uint64_t elapsed = pStack->lastEventTime - entryTime; - addInclusiveTime(caller, method, elapsed); - } - } - caller = &dataKeys->methods[TOPLEVEL_INDEX]; - caller->elapsedInclusive = sumThreadTime; - -#if 0 - fclose(dumpStream); -#endif - - if (threadTime != NULL) { - *threadTime = sumThreadTime; - } - -bail: - if (dataFp != NULL) - fclose(dataFp); - - return dataKeys; -} - -MethodEntry** parseMethodEntries(DataKeys* dataKeys) -{ - int ii; - /* Create a new array of pointers to the methods and sort the pointers - * instead of the actual MethodEntry structs. We need to do this - * because there are other lists that contain pointers to the - * MethodEntry structs. - */ - MethodEntry** pMethods = (MethodEntry**) malloc(sizeof(MethodEntry*) * dataKeys->numMethods); - for (ii = 0; ii < dataKeys->numMethods; ++ii) { - MethodEntry* entry = &dataKeys->methods[ii]; - pMethods[ii] = entry; - } - - return pMethods; -} - -/* - * Produce a function profile from the following methods - */ -void profileTrace(TraceData* traceData, MethodEntry **pMethods, int numMethods, uint64_t sumThreadTime) -{ - /* Print the html header, if necessary */ - if (gOptions.outputHtml) { - printf(htmlHeader, gOptions.sortableUrl); - outputTableOfContents(); - } - - printExclusiveProfile(pMethods, numMethods, sumThreadTime); - printInclusiveProfile(pMethods, numMethods, sumThreadTime); - - createClassList(traceData, pMethods, numMethods); - printClassProfiles(traceData, sumThreadTime); - - createUniqueMethodList(traceData, pMethods, numMethods); - printMethodProfiles(traceData, sumThreadTime); - - if (gOptions.outputHtml) { - printf("%s", htmlFooter); - } -} - -int compareMethodNamesForDiff(const void *a, const void *b) -{ - int result; - - const MethodEntry *methodA = *(const MethodEntry**)a; - const MethodEntry *methodB = *(const MethodEntry**)b; - if (methodA->methodName == NULL || methodB->methodName == NULL) { - return compareClassNames(a, b); - } - result = strcmp(methodA->methodName, methodB->methodName); - if (result == 0) { - result = strcmp(methodA->signature, methodB->signature); - if (result == 0) { - return strcmp(methodA->className, methodB->className); - } - } - return result; -} - -int findMatch(MethodEntry** methods, int size, MethodEntry* matchThis) -{ - int i; - - for (i = 0 ; i < size ; i++) { - MethodEntry* method = methods[i]; - - if (method != NULL && !compareMethodNamesForDiff(&method, &matchThis)) { -// printf("%s.%s == %s.%s<br>\n", matchThis->className, matchThis->methodName, - // method->className, method->methodName); - - return i; -/* if (!compareMethodNames(&method, &matchThis)) { - return i; - } -*/ } - } - - return -1; -} - -int compareDiffEntriesExculsive(const void *a, const void *b) -{ - const DiffEntry* entryA = (const DiffEntry*)a; - const DiffEntry* entryB = (const DiffEntry*)b; - - if (entryA->differenceExclusive < entryB->differenceExclusive) { - return 1; - } else if (entryA->differenceExclusive > entryB->differenceExclusive) { - return -1; - } - - return 0; -} - -int compareDiffEntriesInculsive(const void *a, const void *b) -{ - const DiffEntry* entryA = (const DiffEntry*)a; - const DiffEntry* entryB = (const DiffEntry*)b; - - if (entryA->differenceInclusive < entryB->differenceInclusive) { - return 1; - } else if (entryA->differenceInclusive > entryB->differenceInclusive) { - return -1; - } - - return 0; -} - -void printMissingMethod(MethodEntry* method) -{ - char classBuf[HTML_BUFSIZE]; - char methodBuf[HTML_BUFSIZE]; - char* className; - char* methodName; - - className = htmlEscape(method->className, classBuf, HTML_BUFSIZE); - methodName = htmlEscape(method->methodName, methodBuf, HTML_BUFSIZE); - - if (gOptions.outputHtml) printf("<tr><td>\n"); - - printf("%s.%s ", className, methodName); - if (gOptions.outputHtml) printf("</td><td>"); - - printf("%" PRIu64 " ", method->elapsedExclusive); - if (gOptions.outputHtml) printf("</td><td>"); - - printf("%" PRIu64 " ", method->elapsedInclusive); - if (gOptions.outputHtml) printf("</td><td>"); - - printf("%d\n", method->numCalls[0]); - if (gOptions.outputHtml) printf("</td><td>\n"); -} - - -void createDiff(DataKeys* d1, uint64_t sum1 __unused, - DataKeys* d2, uint64_t sum2 __unused) -{ - MethodEntry** methods1 = parseMethodEntries(d1); - MethodEntry** methods2 = parseMethodEntries(d2); - - // sort and assign the indicies - int i; - qsort(methods1, d1->numMethods, sizeof(MethodEntry*), compareElapsedInclusive); - for (i = 0; i < d1->numMethods; ++i) { - methods1[i]->index = i; - } - - qsort(methods2, d2->numMethods, sizeof(MethodEntry*), compareElapsedInclusive); - for (i = 0; i < d2->numMethods; ++i) { - methods2[i]->index = i; - } - - int max = (d1->numMethods < d2->numMethods) ? d2->numMethods : d1->numMethods; - max++; - DiffEntry* diffs = (DiffEntry*)malloc(max * sizeof(DiffEntry)); - memset(diffs, 0, max * sizeof(DiffEntry)); - DiffEntry* ptr = diffs; - -// printf("<br>d1->numMethods: %d d1->numMethods: %d<br>\n", d1->numMethods, d2->numMethods); - - int matches = 0; - - for (i = 0 ; i < d1->numMethods ; i++) { - int match = findMatch(methods2, d2->numMethods, methods1[i]); - if (match >= 0) { - ptr->method1 = methods1[i]; - ptr->method2 = methods2[match]; - - uint64_t e1 = ptr->method1->elapsedExclusive; - uint64_t e2 = ptr->method2->elapsedExclusive; - if (e1 > 0) { - ptr->differenceExclusive = e2 - e1; - ptr->differenceExclusivePercentage = ((double)e2 / (double)e1) * 100.0; - } - - uint64_t i1 = ptr->method1->elapsedInclusive; - uint64_t i2 = ptr->method2->elapsedInclusive; - if (i1 > 0) { - ptr->differenceInclusive = i2 - i1; - ptr->differenceInclusivePercentage = ((double)i2 / (double)i1) * 100.0; - } - - // clear these out so we don't find them again and we know which ones - // we have left over - methods1[i] = NULL; - methods2[match] = NULL; - ptr++; - - matches++; - } - } - ptr->method1 = NULL; - ptr->method2 = NULL; - - qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesExculsive); - ptr = diffs; - - if (gOptions.outputHtml) { - printf(htmlHeader, gOptions.sortableUrl); - printf("<h3>Table of Contents</h3>\n"); - printf("<ul>\n"); - printf("<li><a href='#exclusive'>Exclusive</a>\n"); - printf("<li><a href='#inclusive'>Inclusive</a>\n"); - printf("</ul>\n"); - printf("Run 1: %s<br>\n", gOptions.diffFileName); - printf("Run 2: %s<br>\n", gOptions.traceFileName); - printf("<a name=\"exclusive\"></a><h3 id=\"exclusive\">Exclusive</h3>\n"); - printf(tableHeader, "exclusive_table"); - } - - char classBuf[HTML_BUFSIZE]; - char methodBuf[HTML_BUFSIZE]; - char* className; - char* methodName; - - while (ptr->method1 != NULL && ptr->method2 != NULL) { - if (gOptions.outputHtml) printf("<tr><td>\n"); - - className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE); - methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE); - - printf("%s.%s ", className, methodName); - if (gOptions.outputHtml) printf("</td><td>"); - - printf("%" PRIu64 " ", ptr->method1->elapsedExclusive); - if (gOptions.outputHtml) printf("</td><td>"); - - printf("%" PRIu64 " ", ptr->method2->elapsedExclusive); - if (gOptions.outputHtml) printf("</td><td>"); - - printf("%" PRIu64 " ", ptr->differenceExclusive); - if (gOptions.outputHtml) printf("</td><td>"); - - printf("%.2f\n", ptr->differenceExclusivePercentage); - if (gOptions.outputHtml) printf("</td><td>\n"); - - printf("%d\n", ptr->method1->numCalls[0]); - if (gOptions.outputHtml) printf("</td><td>\n"); - - printf("%d\n", ptr->method2->numCalls[0]); - if (gOptions.outputHtml) printf("</td></tr>\n"); - - ptr++; - } - - if (gOptions.outputHtml) printf("</table>\n"); - - if (gOptions.outputHtml) { - printf(htmlHeader, gOptions.sortableUrl); - printf("Run 1: %s<br>\n", gOptions.diffFileName); - printf("Run 2: %s<br>\n", gOptions.traceFileName); - printf("<a name=\"inclusive\"></a><h3 id=\"inculisve\">Inclusive</h3>\n"); - printf(tableHeader, "inclusive_table"); - } - - qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesInculsive); - ptr = diffs; - - while (ptr->method1 != NULL && ptr->method2 != NULL) { - if (gOptions.outputHtml) printf("<tr><td>\n"); - - className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE); - methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE); - - printf("%s.%s ", className, methodName); - if (gOptions.outputHtml) printf("</td><td>"); - - printf("%" PRIu64 " ", ptr->method1->elapsedInclusive); - if (gOptions.outputHtml) printf("</td><td>"); - - printf("%" PRIu64 " ", ptr->method2->elapsedInclusive); - if (gOptions.outputHtml) printf("</td><td>"); - - printf("%" PRIu64 " ", ptr->differenceInclusive); - if (gOptions.outputHtml) printf("</td><td>"); - - printf("%.2f\n", ptr->differenceInclusivePercentage); - if (gOptions.outputHtml) printf("</td><td>\n"); - - printf("%d\n", ptr->method1->numCalls[0]); - if (gOptions.outputHtml) printf("</td><td>\n"); - - printf("%d\n", ptr->method2->numCalls[0]); - if (gOptions.outputHtml) printf("</td></tr>\n"); - - ptr++; - } - - if (gOptions.outputHtml) { - printf("</table>\n"); - printf("<h3>Run 1 methods not found in Run 2</h3>"); - printf(tableHeaderMissing, "?"); - } - - for (i = 0; i < d1->numMethods; ++i) { - if (methods1[i] != NULL) { - printMissingMethod(methods1[i]); - } - } - - if (gOptions.outputHtml) { - printf("</table>\n"); - printf("<h3>Run 2 methods not found in Run 1</h3>"); - printf(tableHeaderMissing, "?"); - } - - for (i = 0; i < d2->numMethods; ++i) { - if (methods2[i] != NULL) { - printMissingMethod(methods2[i]); - } - } - - if (gOptions.outputHtml) printf("</body></html\n"); -} - -int usage(const char *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, " -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"); - fprintf(stderr, " -s - URL base to where the sortable javascript file\n"); - fprintf(stderr, " -t threshold - Threshold percentage for including nodes in the graph\n"); - return 2; -} - -// Returns true if there was an error -int parseOptions(int argc, char **argv) -{ - while (1) { - int opt = getopt(argc, argv, "d:hg:kos:t:"); - if (opt == -1) - break; - switch (opt) { - case 'd': - gOptions.diffFileName = optarg; - break; - case 'g': - gOptions.graphFileName = optarg; - break; - case 'k': - gOptions.keepDotFile = 1; - break; - case 'h': - gOptions.outputHtml = 1; - break; - case 'o': - gOptions.dump = 1; - break; - case 's': - gOptions.sortableUrl = optarg; - break; - case 't': - gOptions.threshold = atoi(optarg); - break; - default: - return 1; - } - } - return 0; -} - -/* - * Parse args. - */ -int main(int argc, char** argv) -{ - gOptions.threshold = -1; - - // Parse the options - if (parseOptions(argc, argv) || argc - optind != 1) - return usage(argv[0]); - - gOptions.traceFileName = argv[optind]; - - if (gOptions.threshold < 0 || 100 <= gOptions.threshold) { - gOptions.threshold = 20; - } - - if (gOptions.dump) { - dumpTrace(); - return 0; - } - - uint64_t sumThreadTime = 0; - - TraceData data1; - DataKeys* dataKeys = parseDataKeys(&data1, gOptions.traceFileName, - &sumThreadTime); - if (dataKeys == NULL) { - fprintf(stderr, "Cannot read \"%s\".\n", gOptions.traceFileName); - exit(1); - } - - if (gOptions.diffFileName != NULL) { - uint64_t sum2; - TraceData data2; - DataKeys* d2 = parseDataKeys(&data2, gOptions.diffFileName, &sum2); - if (d2 == NULL) { - fprintf(stderr, "Cannot read \"%s\".\n", gOptions.diffFileName); - exit(1); - } - - createDiff(d2, sum2, dataKeys, sumThreadTime); - - freeDataKeys(d2); - } else { - MethodEntry** methods = parseMethodEntries(dataKeys); - profileTrace(&data1, methods, dataKeys->numMethods, sumThreadTime); - if (gOptions.graphFileName != NULL) { - createInclusiveProfileGraphNew(dataKeys); - } - free(methods); - } - - freeDataKeys(dataKeys); - - return 0; -} diff --git a/tools/dmtracedump/dmtracedump.pl b/tools/dmtracedump/dmtracedump.pl deleted file mode 100755 index 6e487c60a..000000000 --- a/tools/dmtracedump/dmtracedump.pl +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/perl - -opendir(DIR, ".") || die "can't opendir $some_dir: $!"; -@traces = grep { /.*\.dmtrace\.data/ } readdir(DIR); - -foreach (@traces) -{ - $input = $_; - $input =~ s/\.data$//; - - $output = "$input.html"; - - print("dmtracedump -h -p $input > $output\n"); - system("dmtracedump -h -p '$input' > '$output'"); - -} - -closedir DIR; diff --git a/tools/dmtracedump/dumpdir.sh b/tools/dmtracedump/dumpdir.sh deleted file mode 100644 index 81992a2a8..000000000 --- a/tools/dmtracedump/dumpdir.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -FILES=`ls $1/*.data | sed "s/^\\(.*\\).data$/\\1/"` - -mkdir -p $2 - -for F in $FILES -do - G=$2/`echo $F | sed "s/.*\\///g"`.html - dmtracedump -h -p $F > $G -done |