(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 (int32_t 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 (int32_t 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;
}
/*
* Read values from the binary data file.
*/
/*
* Make the return value "uint32_t" instead of "uint16_t" so that we can detect EOF.
*/
uint32_t read2LE(FILE* fp) {
uint32_t val = getc(fp);
val |= getc(fp) << 8;
return val;
}
uint32_t read4LE(FILE* fp) {
uint32_t val = getc(fp);
val |= getc(fp) << 8;
val |= getc(fp) << 16;
val |= getc(fp) << 24;
return val;
}
uint64_t read8LE(FILE* fp) {
uint64_t val = getc(fp);
val |= (uint64_t) getc(fp) << 8;
val |= (uint64_t) getc(fp) << 16;
val |= (uint64_t) getc(fp) << 24;
val |= (uint64_t) getc(fp) << 32;
val |= (uint64_t) getc(fp) << 40;
val |= (uint64_t) getc(fp) << 48;
val |= (uint64_t) getc(fp) << 56;
return val;
}
/*
* Parse the header of the data section.
*
* Returns with the file positioned at the start of the record data.
*/
int32_t parseDataHeader(FILE* fp, DataHeader* pHeader) {
pHeader->magic = read4LE(fp);
pHeader->version = read2LE(fp);
pHeader->offsetToData = read2LE(fp);
pHeader->startWhen = read8LE(fp);
int32_t 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 nullptr if no matching method was found.
*/
MethodEntry* lookupMethod(DataKeys* pKeys, int64_t methodId) {
int32_t lo = 0;
int32_t hi = pKeys->numMethods - 1;
while (hi >= lo) {
int32_t mid = (hi + lo) / 2;
int64_t 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 nullptr;
}
/*
* 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.
*/
int32_t readDataRecord(FILE* dataFp, DataHeader* dataHeader, int32_t* threadId,
uint32_t* methodVal, uint64_t* elapsedTime) {
int32_t id;
int32_t 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, {nullptr, nullptr}, {nullptr, nullptr}, {0, 0}, 0, 0, -1};
char bogusBuf[80];
TraceData traceData;
// printf("Dumping '%s' '%s'\n", dataFileName, keyFileName);
char spaces[MAX_STACK_DEPTH + 1];
memset(spaces, '.', MAX_STACK_DEPTH);
spaces[MAX_STACK_DEPTH] = '\0';
for (int32_t i = 0; i < MAX_THREADS; i++)
traceData.depth[i] = 2; // adjust for return from start function
FILE* dataFp = fopen(gOptions.traceFileName, "rb");
if (dataFp == nullptr) return;
DataKeys* pKeys = parseKeys(dataFp, 1);
if (pKeys == nullptr) {
fclose(dataFp);
return;
}
DataHeader dataHeader;
if (parseDataHeader(dataFp, &dataHeader) < 0) {
fclose(dataFp);
freeDataKeys(pKeys);
return;
}
printf("Trace (threadID action usecs class.method signature):\n");
while (1) {
/*
* Extract values from file.
*/
int32_t threadId;
uint32_t methodVal;
uint64_t elapsedTime;
if (readDataRecord(dataFp, &dataHeader, &threadId, &methodVal, &elapsedTime))
break;
int32_t action = METHOD_ACTION(methodVal);
int64_t methodId = METHOD_ID(methodVal);
/*
* Generate a line of output.
*/
int64_t lastEnter = 0;
int32_t mismatch = 0;
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;
}
int32_t printDepth = traceData.depth[threadId];
char depthNote = ' ';
if (printDepth < 0) {
printDepth = 0;
depthNote = '-';
} else if (printDepth > MAX_STACK_DEPTH) {
printDepth = MAX_STACK_DEPTH;
depthNote = '+';
}
MethodEntry* method = lookupMethod(pKeys, methodId);
if (method == nullptr) {
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;
}
fclose(dataFp);
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) {
#if 0
bool verbose = false;
if (strcmp(child->className, debugClassName) == 0)
verbose = true;
#endif
int32_t childIsRecursive = (child->recursiveEntries > 0);
int32_t 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* pTimed;
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 == nullptr) {
/* Allocate a new TimedMethod */
pTimed = new 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 == nullptr) {
/* Allocate a new TimedMethod */
pTimed = new 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, int32_t* num) {
/* Count the elements */
TimedMethod* pTimed;
int32_t num_entries = 0;
for (pTimed = list; pTimed; pTimed = pTimed->next) num_entries += 1;
*num = num_entries;
if (num_entries == 0) return nullptr;
/* Copy all the list elements to a new array and sort them */
int32_t ii;
TimedMethod* sorted = new 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 = nullptr;
return sorted;
}
/* Define flag values for printInclusiveMethod() */
static const int32_t 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, int32_t numCalls, int32_t flags) {
char buf[80];
const char* anchor_close = "";
const char* spaces = " "; /* 6 spaces */
int32_t num_spaces = strlen(spaces);
const char* space_ptr = &spaces[num_spaces];
char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
char signatureBuf[HTML_BUFSIZE];
if (gOptions.outputHtml) anchor_close = "";
int32_t num;
TimedMethod* sorted = sortTimedMethodList(list, &num);
double methodTotal = method->elapsedInclusive;
for (TimedMethod* pTimed = sorted; pTimed; pTimed = pTimed->next) {
MethodEntry* relative = pTimed->method;
const char* className = relative->className;
const char* methodName = relative->methodName;
const char* signature = relative->signature;
double per = 100.0 * pTimed->elapsedInclusive / methodTotal;
sprintf(buf, "[%d]", relative->index);
if (gOptions.outputHtml) {
int32_t len = strlen(buf);
if (len > num_spaces) len = num_spaces;
sprintf(buf, "[%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);
}
int32_t 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, int32_t top, MethodEntry* method) {
method->recursiveEntries = 0;
for (int32_t ii = 0; ii < top; ++ii) {
if (pStack->calls[ii].method == method) method->recursiveEntries += 1;
}
}
void stackDump(CallStack* pStack, int32_t top) {
for (int32_t 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("\n");
printf("Table of Contents
\n");
printf("\n\n");
}
void outputNavigationBar() {
printf("[Top]\n");
printf("[Exclusive]\n");
printf("[Inclusive]\n");
printf("[Class]\n");
printf("[Method]\n");
printf("
\n");
}
void printExclusiveProfile(MethodEntry** pMethods, int32_t numMethods, uint64_t sumThreadTime) {
char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
char signatureBuf[HTML_BUFSIZE];
const char* anchor_close = "";
char anchor_buf[80];
anchor_buf[0] = 0;
if (gOptions.outputHtml) {
anchor_close = "";
printf("\n");
printf("
\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 (int32_t 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("
\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("
\n\n");
}
printf(" Usecs self %% sum %% Method\n");
double sum = 0;
double total = sumThreadTime;
for (int32_t ii = 0; ii < numMethods; ++ii) {
MethodEntry* method = pMethods[ii];
/* Don't show methods with zero cycles */
if (method->elapsedExclusive == 0) break;
const char* className = method->className;
const char* methodName = method->methodName;
const char* signature = method->signature;
sum += method->elapsedExclusive;
double per = 100.0 * method->elapsedExclusive / total;
double sum_per = 100.0 * sum / total;
if (gOptions.outputHtml) {
sprintf(anchor_buf, "", 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("
\n");
}
}
/* check to make sure that the child method meets the threshold of the parent */
int32_t 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;
for (TimedMethod* 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;
for (TimedMethod* 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", (int32_t)time(nullptr), 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, int32_t numMethods, uint64_t sumThreadTime) {
char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
char signatureBuf[HTML_BUFSIZE];
char anchor_buf[80];
const char* anchor_close = "";
anchor_buf[0] = 0;
if (gOptions.outputHtml) {
anchor_close = "";
printf("\n");
printf("
\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("
\n\n");
}
printf("index %%/total %%/self index calls usecs name\n");
double total = sumThreadTime;
for (int32_t ii = 0; ii < numMethods; ++ii) {
char buf[40];
MethodEntry* method = pMethods[ii];
/* Don't show methods with zero cycles */
if (method->elapsedInclusive == 0) break;
const char* className = method->className;
const char* methodName = method->methodName;
const char* signature = method->signature;
if (gOptions.outputHtml) {
printf("", 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 */
int32_t 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);
}
double 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);
}
double 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("
\n");
}
}
void createClassList(TraceData* traceData, MethodEntry** pMethods, int32_t numMethods) {
/* 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 = nullptr;
traceData->numClasses = 0;
for (int32_t ii = 0; ii < numMethods; ++ii) {
if (pMethods[ii]->methodName == nullptr) {
continue;
}
if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
// Remember the first one
if (firstClassName == nullptr) {
firstClassName = pMethods[ii]->className;
}
traceData->numClasses += 1;
currentClassName = pMethods[ii]->className;
}
}
if (traceData->numClasses == 0) {
traceData->classes = nullptr;
return;
}
/* Allocate space for all of the unique class names */
traceData->classes = new ClassEntry[traceData->numClasses];
/* Initialize the classes array */
memset(traceData->classes, 0, sizeof(ClassEntry) * traceData->numClasses);
ClassEntry* pClass = traceData->classes;
pClass->className = currentClassName = firstClassName;
int32_t prevNumMethods = 0;
for (int32_t ii = 0; ii < numMethods; ++ii) {
if (pMethods[ii]->methodName == nullptr) {
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 = nullptr;
currentClassName = "";
int32_t nextMethod = 0;
for (int32_t ii = 0; ii < numMethods; ++ii) {
if (pMethods[ii]->methodName == nullptr) {
continue;
}
if (strcmp(pMethods[ii]->className, currentClassName) != 0) {
currentClassName = pMethods[ii]->className;
if (pClass == nullptr)
pClass = traceData->classes;
else
pClass++;
/* Allocate space for the methods array */
pClass->methods = new MethodEntry*[pClass->numMethods];
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, int32_t width) {
int32_t leadingSpaces = 1;
if (width < 0) {
width = -width;
leadingSpaces = 0;
}
int32_t len = strlen(buf);
int32_t numSpaces = width - len;
if (numSpaces <= 0) {
printf("%s", buf);
return;
}
if (leadingSpaces == 0) printf("%s", buf);
for (int32_t ii = 0; ii < numSpaces; ++ii) printf(" ");
if (leadingSpaces == 1) printf("%s", buf);
}
void printClassProfiles(TraceData* traceData, uint64_t sumThreadTime) {
char classBuf[HTML_BUFSIZE];
char methodBuf[HTML_BUFSIZE];
char signatureBuf[HTML_BUFSIZE];
if (gOptions.outputHtml) {
printf("\n");
printf("
\n");
outputNavigationBar();
} else {
printf("\n%s\n", profileSeparator);
}
if (traceData->numClasses == 0) {
printf("\nNo classes.\n");
if (gOptions.outputHtml) {
printf("
\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("
\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 (int32_t ii = 0; ii < traceData->numClasses; ++ii, ++pClass) {
// printf("%s %d methods\n", pClass->className, pClass->numMethods);
int32_t numMethods = pClass->numMethods;
for (int32_t jj = 0; jj < numMethods; ++jj) {
MethodEntry* 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 = new ClassEntry*[traceData->numClasses];
for (int32_t 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(
"\n");
} else {
printf(" Cycles %%/total Cumul.%% Calls+Recur Class\n");
}
double sum = 0;
double total = sumThreadTime;
for (int32_t ii = 0; ii < traceData->numClasses; ++ii) {
/* Skip classes with zero cycles */
pClass = pClasses[ii];
if (pClass->elapsedExclusive == 0) break;
sum += pClass->elapsedExclusive;
double per = 100.0 * pClass->elapsedExclusive / total;
double sum_per = 100.0 * sum / total;
const char* className = pClass->className;
if (gOptions.outputHtml) {
char buf[80];
className = htmlEscape(className, classBuf, HTML_BUFSIZE);
printf(
"+",
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("
\n");
printf("\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);
}
int32_t numMethods = pClass->numMethods;
double classExclusive = pClass->elapsedExclusive;
double sumMethods = 0;
for (int32_t jj = 0; jj < numMethods; ++jj) {
MethodEntry* method = pClass->methods[jj];
const char* methodName = method->methodName;
const char* signature = 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("
");
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("
[%d] %s %s", method->index,
method->index, methodName, signature);
printf("
\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("
\n");
}
}
}
void createUniqueMethodList(TraceData* traceData, MethodEntry** pMethods, int32_t numMethods) {
/* 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 (int32_t ii = 0; ii < numMethods; ++ii) {
if (pMethods[ii]->methodName == nullptr) 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 */
traceData->uniqueMethods = new UniqueMethodEntry[traceData->numUniqueMethods];
/* Initialize the uniqueMethods array */
memset(traceData->uniqueMethods, 0, sizeof(UniqueMethodEntry) * traceData->numUniqueMethods);
UniqueMethodEntry* pUnique = traceData->uniqueMethods;
currentMethodName = nullptr;
int32_t prevNumMethods = 0;
for (int32_t ii = 0; ii < numMethods; ++ii) {
if (pMethods[ii]->methodName == nullptr) continue;
if (currentMethodName == nullptr) 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 = nullptr;
currentMethodName = "";
int32_t nextMethod = 0;
for (int32_t ii = 0; ii < numMethods; ++ii) {
if (pMethods[ii]->methodName == nullptr) continue;
if (strcmp(pMethods[ii]->methodName, currentMethodName) != 0) {
currentMethodName = pMethods[ii]->methodName;
if (pUnique == nullptr)
pUnique = traceData->uniqueMethods;
else
pUnique++;
/* Allocate space for the methods array */
pUnique->methods = new MethodEntry*[pUnique->numMethods];
nextMethod = 0;
}
pUnique->methods[nextMethod++] = pMethods[ii];
}
}
void printMethodProfiles(TraceData* traceData, uint64_t sumThreadTime) {
char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
char signatureBuf[HTML_BUFSIZE];
if (traceData->numUniqueMethods == 0) return;
if (gOptions.outputHtml) {
printf("\n");
printf("
\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("
\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 (int32_t ii = 0; ii < traceData->numUniqueMethods; ++ii, ++pUnique) {
int32_t numMethods = pUnique->numMethods;
for (int32_t jj = 0; jj < numMethods; ++jj) {
MethodEntry* 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 = new UniqueMethodEntry*[traceData->numUniqueMethods];
for (int32_t 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(
"\n");
} else {
printf(" Cycles %%/total Cumul.%% Calls+Recur Method\n");
}
double sum = 0;
double total = sumThreadTime;
for (int32_t ii = 0; ii < traceData->numUniqueMethods; ++ii) {
/* Skip methods with zero cycles */
pUnique = pUniqueMethods[ii];
if (pUnique->elapsedExclusive == 0) break;
sum += pUnique->elapsedExclusive;
double per = 100.0 * pUnique->elapsedExclusive / total;
double sum_per = 100.0 * sum / total;
const char* methodName = pUnique->methods[0]->methodName;
if (gOptions.outputHtml) {
char buf[80];
methodName = htmlEscape(methodName, methodBuf, HTML_BUFSIZE);
printf(
"+",
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("
\n");
printf("\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);
}
int32_t numMethods = pUnique->numMethods;
double methodExclusive = pUnique->elapsedExclusive;
double sumMethods = 0;
for (int32_t jj = 0; jj < numMethods; ++jj) {
MethodEntry* method = pUnique->methods[jj];
const char* className = method->className;
const char* signature = 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("
");
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("
[%d] %s.%s %s", method->index,
method->index, className, methodName, signature);
printf("
\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("
\n");
}
}
}
/*
* Read the key and data files and return the MethodEntries for those files
*/
DataKeys* parseDataKeys(TraceData* traceData, const char* traceFileName, uint64_t* threadTime) {
MethodEntry* caller;
FILE* dataFp = fopen(traceFileName, "rb");
if (dataFp == nullptr) return nullptr;
DataKeys* dataKeys = parseKeys(dataFp, 0);
if (dataKeys == nullptr) {
fclose(dataFp);
return nullptr;
}
DataHeader dataHeader;
if (parseDataHeader(dataFp, &dataHeader) < 0) {
fclose(dataFp);
return dataKeys;
}
#if 0
FILE* dumpStream = fopen("debug", "w");
#endif
while (1) {
/*
* Extract values from file.
*/
int32_t threadId;
uint32_t methodVal;
uint64_t currentTime;
if (readDataRecord(dataFp, &dataHeader, &threadId, &methodVal, ¤tTime))
break;
int32_t action = METHOD_ACTION(methodVal);
int64_t methodId = METHOD_ID(methodVal);
/* Get the call stack for this thread */
CallStack* pStack = traceData->stacks[threadId];
/* If there is no call stack yet for this thread, then allocate one */
if (pStack == nullptr) {
pStack = new CallStack();
pStack->top = 0;
pStack->lastEventTime = currentTime;
pStack->threadStartTime = currentTime;
traceData->stacks[threadId] = pStack;
}
/* Lookup the current method */
MethodEntry* method = lookupMethod(dataKeys, methodId);
if (method == nullptr) 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.
*/
uint64_t sumThreadTime = 0;
for (int32_t threadId = 0; threadId < MAX_THREADS; ++threadId) {
CallStack* pStack = traceData->stacks[threadId];
/* If this thread never existed, then continue with next thread */
if (pStack == nullptr) continue;
/* Also, add up the time taken by all of the threads */
sumThreadTime += pStack->lastEventTime - pStack->threadStartTime;
for (int32_t ii = 0; ii < pStack->top; ++ii) {
if (ii == 0)
caller = &dataKeys->methods[TOPLEVEL_INDEX];
else
caller = pStack->calls[ii - 1].method;
MethodEntry* 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 != nullptr) {
*threadTime = sumThreadTime;
}
fclose(dataFp);
return dataKeys;
}
MethodEntry** parseMethodEntries(DataKeys* dataKeys) {
/* 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 = new MethodEntry*[dataKeys->numMethods];
for (int32_t 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, int32_t 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);
}
}
int32_t compareMethodNamesForDiff(const void* a, const void* b) {
const MethodEntry* methodA = *(const MethodEntry**) a;
const MethodEntry* methodB = *(const MethodEntry**) b;
if (methodA->methodName == nullptr || methodB->methodName == nullptr) {
return compareClassNames(a, b);
}
int32_t 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;
}
int32_t findMatch(MethodEntry** methods, int32_t size, MethodEntry* matchThis) {
for (int32_t i = 0; i < size; i++) {
MethodEntry* method = methods[i];
if (method != nullptr && !compareMethodNamesForDiff(&method, &matchThis)) {
// printf("%s.%s == %s.%s
\n", matchThis->className, matchThis->methodName,
// method->className, method->methodName);
return i;
// if (!compareMethodNames(&method, &matchThis)) return i;
}
}
return -1;
}
int32_t 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;
}
int32_t 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 = htmlEscape(method->className, classBuf, HTML_BUFSIZE);
char* methodName = htmlEscape(method->methodName, methodBuf, HTML_BUFSIZE);
if (gOptions.outputHtml) printf("\n");
printf("%s.%s ", className, methodName);
if (gOptions.outputHtml) printf(" | ");
printf("%" PRIu64 " ", method->elapsedExclusive);
if (gOptions.outputHtml) printf(" | ");
printf("%" PRIu64 " ", method->elapsedInclusive);
if (gOptions.outputHtml) printf(" | ");
printf("%d\n", method->numCalls[0]);
if (gOptions.outputHtml) printf(" | \n");
}
void createDiff(DataKeys* d1, DataKeys* d2) {
MethodEntry** methods1 = parseMethodEntries(d1);
MethodEntry** methods2 = parseMethodEntries(d2);
// sort and assign the indicies
qsort(methods1, d1->numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
for (int32_t i = 0; i < d1->numMethods; ++i) {
methods1[i]->index = i;
}
qsort(methods2, d2->numMethods, sizeof(MethodEntry*), compareElapsedInclusive);
for (int32_t i = 0; i < d2->numMethods; ++i) {
methods2[i]->index = i;
}
int32_t max = (d1->numMethods < d2->numMethods) ? d2->numMethods : d1->numMethods;
max++;
DiffEntry* diffs = new DiffEntry[max];
memset(diffs, 0, max * sizeof(DiffEntry));
DiffEntry* ptr = diffs;
// printf(" d1->numMethods: %d d1->numMethods: %d \n",
// d1->numMethods, d2->numMethods);
int32_t matches = 0;
for (int32_t i = 0; i < d1->numMethods; i++) {
int32_t 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 = (static_cast(e2) /
static_cast(e1)) * 100.0;
}
uint64_t i1 = ptr->method1->elapsedInclusive;
uint64_t i2 = ptr->method2->elapsedInclusive;
if (i1 > 0) {
ptr->differenceInclusive = i2 - i1;
ptr->differenceInclusivePercentage = (static_cast(i2) /
static_cast(i1)) * 100.0;
}
// clear these out so we don't find them again and we know which ones
// we have left over
methods1[i] = nullptr;
methods2[match] = nullptr;
ptr++;
matches++;
}
}
ptr->method1 = nullptr;
ptr->method2 = nullptr;
qsort(diffs, matches, sizeof(DiffEntry), compareDiffEntriesExculsive);
ptr = diffs;
if (gOptions.outputHtml) {
printf(htmlHeader, gOptions.sortableUrl);
printf("Table of Contents\n");
printf("\n");
printf("Run 1: %s \n", gOptions.diffFileName);
printf("Run 2: %s \n", gOptions.traceFileName);
printf("Exclusive\n");
printf(tableHeader, "exclusive_table");
}
char classBuf[HTML_BUFSIZE];
char methodBuf[HTML_BUFSIZE];
while (ptr->method1 != nullptr && ptr->method2 != nullptr) {
if (gOptions.outputHtml) printf("\n");
char* className = htmlEscape(ptr->method1->className, classBuf, HTML_BUFSIZE);
char* methodName = htmlEscape(ptr->method1->methodName, methodBuf, HTML_BUFSIZE);
printf("%s.%s ", className, methodName);
if (gOptions.outputHtml) printf(" | ");
printf("%" PRIu64 " ", ptr->method1->elapsedExclusive);
if (gOptions.outputHtml) printf(" | ");
printf("%" PRIu64 " ", ptr->method2->elapsedExclusive);
if (gOptions.outputHtml) printf(" | ");
printf("%" PRIu64 " ", ptr->differenceExclusive);
if (gOptions.outputHtml) printf(" | ");
printf("%.2f\n", ptr->differenceExclusivePercentage);
if (gOptions.outputHtml) printf(" | \n");
printf("%d\n", ptr->method1->numCalls[0]);
if (gOptions.outputHtml) printf(" | \n");
printf("%d\n", ptr->method2->numCalls[0]);
if (gOptions.outputHtml) printf(" | \n");
ptr++;
}
if (gOptions.outputHtml) printf(" |