Fix for unmatched entry/exit points in test traces

Added support for the creation of test traces with unmatched entries/exits (current implementation segfaulted in some cases)
diff --git a/tools/dmtracedump/CreateTestTrace.c b/tools/dmtracedump/CreateTestTrace.c
index 256f21a..9d72b1f 100644
--- a/tools/dmtracedump/CreateTestTrace.c
+++ b/tools/dmtracedump/CreateTestTrace.c
@@ -44,6 +44,7 @@
 
 #define VERSION 2
 int versionNumber = VERSION;
+int verbose = 0;
 
 DataHeader header = { 0x574f4c53, VERSION, sizeof(DataHeader), 0LL };
 
@@ -52,22 +53,22 @@
 
 char *keyThreads =
 "*threads\n"
-"1	main\n"
-"2	foo\n"
-"3	bar\n"
-"4	blah\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;
+    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;
@@ -76,9 +77,8 @@
 char buf[BUF_SIZE];
 
 typedef struct stack {
-    dataRecord	**frames;
-    int		nextFrame;
-    int		indentLevel;
+    dataRecord  **frames;
+    int         indentLevel;
 } stack;
 
 /* Mac OS doesn't have strndup(), so implement it here.
@@ -124,7 +124,6 @@
     int nextRecord = 0;
     int indentLevel = 0;
     stack *callStack;
-    int nextFrame = 0;
 
     FILE *inputFp = fopen(inputFileName, "r");
     if (inputFp == NULL) {
@@ -133,13 +132,15 @@
     }
 
     /* Count the number of lines in the buffer */
-    int numLines = 0;
+    int numRecords = 0;
     int maxThreadId = 1;
+    int maxFrames = 0;
+    char *indentEnd;
     while (fgets(buf, BUF_SIZE, inputFp)) {
         char *cp = buf;
         if (*cp == '#')
             continue;
-        numLines += 1;
+        numRecords += 1;
         if (isdigit(*cp)) {
             int time = strtoul(cp, &cp, 0);
             while (isspace(*cp))
@@ -148,17 +149,22 @@
             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 */
-    numLines += 1;
-    records = (dataRecord *) malloc(sizeof(dataRecord) * numLines);
+    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].nextFrame = 0;
+        callStack[ii].indentLevel = 0;
     }
 
     rewind(inputFp);
@@ -169,9 +175,12 @@
 
         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.
@@ -189,10 +198,9 @@
         // Allocate space for the thread stack, if necessary
         if (callStack[threadId].frames == NULL) {
             dataRecord **stk;
-            stk = (dataRecord **) malloc(sizeof(dataRecord *) * numLines);
+            stk = (dataRecord **) malloc(sizeof(dataRecord *) * maxFrames);
             callStack[threadId].frames = stk;
         }
-        nextFrame = callStack[threadId].nextFrame;
         indentLevel = callStack[threadId].indentLevel;
 
         save_cp = cp;
@@ -242,58 +250,66 @@
             }
         }
 
+        if (verbose) {
+            printf("Indent: %d; IndentLevel: %d; Line: %s", indent, indentLevel, buf);
+        }
+
         action = 0;
-        if (indent == indentLevel + 1) {
-            callStack[threadId].frames[nextFrame++] = &records[nextRecord];
-        } else if (indent == indentLevel) {
-            char *name = callStack[threadId].frames[nextFrame - 1]->fullName;
-            if (strcmp(name, records[nextRecord].fullName) == 0) {
-                nextFrame -= 1;
+        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 (nextFrame == indentLevel) {
+                if (indentLevel < 1) {
                     fprintf(stderr, "Error: line %d: %s", linenum, buf);
-                    fprintf(stderr, "  expected exit from %s\n",
-                            callStack[threadId].frames[nextFrame - 1]->fullName);
+                    fprintf(stderr, "  expected positive (>0) indentation, found %d\n",
+                            indent);
                     exit(1);
-                } else {
-                    callStack[threadId].frames[nextFrame++] = &records[nextRecord];
                 }
-            }
-        } else if (indent == indentLevel - 1) {
-            action = 1;
-            // Allow popping frames past the bottom of the stack.
-            if (nextFrame > 0) {
-                char *name = callStack[threadId].frames[nextFrame - 1]->fullName;
+                char *name = callStack[threadId].frames[indentLevel - 1]->fullName;
                 if (strcmp(name, records[nextRecord].fullName) == 0) {
-                    nextFrame -= 1;
-                } else {
+                    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[nextFrame - 1]->fullName);
+                            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",
+                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.
-            callStack[threadId].frames[nextFrame++] = &records[nextRecord];
-            indentLevel = indent;
+            indentLevel = indent - 1;
+            callStack[threadId].frames[indentLevel] = &records[nextRecord];
         }
+
         if (action == 0)
             indentLevel += 1;
         else
             indentLevel -= 1;
         records[nextRecord].action = action;
-        callStack[threadId].nextFrame = nextFrame;
         callStack[threadId].indentLevel = indentLevel;
 
         nextRecord += 1;
@@ -383,14 +399,14 @@
                 pNext->methodId = id;
         }
         if (pRecord->className == NULL || pRecord->methodName == NULL) {
-            fprintf(keyFp, "0x%x	%s	m	()\n",
+            fprintf(keyFp, "0x%x        %s      m       ()\n",
                     pRecord->methodId, pRecord->fullName);
         } else if (pRecord->signature == NULL) {
-            fprintf(keyFp, "0x%x	%s	%s	()\n",
+            fprintf(keyFp, "0x%x        %s      %s      ()\n",
                     pRecord->methodId, pRecord->className,
                     pRecord->methodName);
         } else {
-            fprintf(keyFp, "0x%x	%s	%s	%s\n",
+            fprintf(keyFp, "0x%x        %s      %s      %s\n",
                     pRecord->methodId, pRecord->className,
                     pRecord->methodName, pRecord->signature);
         }
@@ -432,7 +448,7 @@
 {
     int err = 0;
     while (1) {
-        int opt = getopt(argc, argv, "v:");
+        int opt = getopt(argc, argv, "v:d");
         if (opt == -1)
             break;
         switch (opt) {
@@ -444,6 +460,9 @@
                     err = 1;
                 }
                 break;
+            case 'd':
+                verbose = 1;
+                break;
             default:
                 err = 1;
                 break;
@@ -459,7 +478,7 @@
     int len;
 
     if (parseOptions(argc, argv) || argc - optind != 2) {
-        fprintf(stderr, "Usage: %s [-v version] input_file trace_prefix\n",
+        fprintf(stderr, "Usage: %s [-v version] [-d] input_file trace_prefix\n",
                 argv[0]);
         exit(1);
     }