Add a JIT stats object in the shell
authorBrian Crowder <crowder@fiverocks.com>
Fri, 19 Sep 2008 22:47:58 -0400
changeset 19592 a6dc830e0ad910b6ebb38971ce4b24124c0ee52f
parent 19591 1394251eb58b399caa6d6fe9c411480dff84bd63
child 19593 c2a3ef237ca2123a896b2662fb50b5b1b15afc25
push idunknown
push userunknown
push dateunknown
milestone1.9.1b1pre
Add a JIT stats object in the shell
js/src/js.cpp
js/src/jstracer.cpp
js/src/trace-test.js
--- a/js/src/js.cpp
+++ b/js/src/js.cpp
@@ -457,16 +457,23 @@ ProcessArgs(JSContext *cx, JSObject *obj
             break;
 
         case 'x':
             JS_ToggleOptions(cx, JSOPTION_XML);
             break;
 
         case 'j':
             JS_ToggleOptions(cx, JSOPTION_JIT);
+#ifdef DEBUG
+extern struct JSClass jitstats_class;
+extern void js_InitJITStatsClass(JSContext *cx, JSObject *glob);
+            js_InitJITStatsClass(cx, JS_GetGlobalObject(cx));
+            JS_DefineObject(cx, JS_GetGlobalObject(cx), "tracemonkey",
+                            &jitstats_class, NULL, 0);
+#endif
             break;
             
         case 'o':
             if (++i == argc)
                 return usage();
 
             for (j = 0; js_options[j].name; ++j) {
                 if (strcmp(js_options[j].name, argv[i]) == 0) {
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -67,19 +67,17 @@
 #include "jsregexp.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jstracer.h"
 
 #include "jsautooplen.h"        // generated headers last
 
 /* Number of iterations of a loop where we start tracing.  That is, we don't
-   start tracing until the beginning of the HOTLOOP-th iteration.  If you
-   change this value, make sure to update all the tests in trace-test.js that
-   depend on it.  */
+   start tracing until the beginning of the HOTLOOP-th iteration. */
 #define HOTLOOP 2
 
 /* Number of times we wait to exit on a side exit before we try to extend the tree. */
 #define HOTEXIT 1
 
 /* Max call depths for inlining. */
 #define MAX_CALLDEPTH 10
 
@@ -97,23 +95,89 @@
 
 #ifdef DEBUG
 #define ABORT_TRACE(msg)   do { debug_only_v(fprintf(stdout, "abort: %d: %s\n", __LINE__, msg);)  return false; } while (0)
 #else
 #define ABORT_TRACE(msg)   return false
 #endif
 
 #ifdef DEBUG
-static struct {
-    uint64
-        recorderStarted, recorderAborted, traceCompleted, sideExitIntoInterpreter,
-        typeMapMismatchAtEntry, returnToDifferentLoopHeader, traceTriggered,
-        globalShapeMismatchAtEntry, treesTrashed, slotPromoted,
-        unstableLoopVariable, breakLoopExits, returnLoopExits;
+struct __jitstats {
+#define JITSTAT(x) uint64 x;
+#include "jitstats.tbl"
+#undef JITSTAT
 } stat = { 0LL, };
+
+JS_STATIC_ASSERT(sizeof(stat) % sizeof(uint64) == 0);
+
+enum jitstat_ids {
+#define JITSTAT(x) STAT ## x ## ID,
+#include "jitstats.tbl"
+#undef JITSTAT
+};
+
+static JSPropertySpec jitstats_props[] = {
+#define JITSTAT(x) { #x, STAT ## x ## ID, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT },
+#include "jitstats.tbl"
+#undef JITSTAT
+    { 0 }
+};
+
+static JSBool
+jitstats_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
+{
+    int index = -1;
+
+    if (JSVAL_IS_STRING(id)) {
+        JSString* str = JSVAL_TO_STRING(id);
+        if (strcmp(JS_GetStringBytes(str), "HOTLOOP") == 0) {
+            *vp = INT_TO_JSVAL(HOTLOOP);
+            return JS_TRUE;
+        }
+    }
+
+    if (JSVAL_IS_INT(id))
+        index = JSVAL_TO_INT(id);
+
+    uint64 result = 0;
+    switch (index) {
+#define JITSTAT(x) case STAT ## x ## ID: result = stat.x; break;
+#include "jitstats.tbl"
+#undef JITSTAT
+    default:
+        *vp = JSVAL_VOID;
+        return JS_TRUE;
+    }
+
+    if (result < JSVAL_INT_MAX) {
+        *vp = INT_TO_JSVAL(result);
+        return JS_TRUE;
+    }
+    char retstr[64];
+    snprintf(retstr, JS_ARRAY_LENGTH(retstr), "%llu", result);
+    *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, retstr));
+    return JS_TRUE;
+}
+
+JSClass jitstats_class = {
+    "jitstats",
+    JSCLASS_HAS_PRIVATE,
+    JS_PropertyStub,       JS_PropertyStub,
+    jitstats_getProperty,  JS_PropertyStub,
+    JS_EnumerateStub,      JS_ResolveStub,
+    JS_ConvertStub,        JS_FinalizeStub,
+    JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+void
+js_InitJITStatsClass(JSContext *cx, JSObject *glob)
+{
+    JS_InitClass(cx, glob, NULL, &jitstats_class, NULL, 0, jitstats_props, NULL, NULL, NULL);
+}
+
 #define AUDIT(x) (stat.x++)
 #else
 #define AUDIT(x) ((void)0)
 #endif
 
 #define INS_CONST(c)    addName(lir->insImm(c), #c)
 #define INS_CONSTPTR(p) addName(lir->insImmPtr((void*) (p)), #p)
 
--- a/js/src/trace-test.js
+++ b/js/src/trace-test.js
@@ -1,40 +1,74 @@
 /**
  * A number of the tests in this file depend on the setting of
  * HOTLOOP.  Define some constants up front, so they're easy to grep
  * for.
  */
 // The HOTLOOP constant we depend on
-const HOTLOOP = 2;
+const HOTLOOP = jitstats.HOTLOOP;
 // The loop count at which we trace
 const RECORDLOOP = HOTLOOP;
 // The loop count at which we run the trace
 const RUNLOOP = HOTLOOP + 1;
 
 var testName = null;
 if ("arguments" in this && arguments.length > 0)
   testName = arguments[0];
 var fails = [], passes=[];
 
 function test(f)
 {
-  if (!testName || testName == f.name)
-    check(f.name, f(), f.expected);
+  if (!testName || testName == f.name) {
+    // Collect our jit stats
+    var localJITstats = {};
+    if (!f.jitstats)
+      f.jitstats = null;
+    for (var propName in jitstats) {
+      localJITstats[propName] = jitstats[propName];
+    }
+    check(f.name, f(), f.expected, localJITstats, f.jitstats);
+  }
 }
 
-function check(desc, actual, expected)
+function check(desc, actual, expected, oldJITstats, expectedJITstats)
 {
   if (expected == actual) {
-    passes.push(desc);
-    return print(desc, ": passed");
+    var pass = true;
+    for (var propName in expectedJITstats) {
+      if (expectedJITstats[propName] !=
+            jitstats[propName] - oldJITstats[propName]) {
+        pass = false;
+        break;
+      }
+    }
+    if (pass) {
+      passes.push(desc);
+      return print(desc, ": passed");
+    }
   }
   fails.push(desc);
-  print(desc, ": FAILED: expected", typeof(expected), "(", expected, ") != actual",
-	typeof(actual), "(", actual, ")");
+  var expectedStats = "";
+  for (var propName in expectedJITstats) {
+    if (expectedStats)
+      expectedStats += " ";
+    expectedStats += propName + ": " + expectedJITstats[propName];
+  }
+  var actualStats = "";
+  for (var propName in expectedJITstats) {
+    if (actualStats)
+      actualStats += " ";
+    actualStats +=
+      propName + ": " + (jitstats[propName] - oldJITstats[propName]);
+  }
+  print(desc, ": FAILED: expected", typeof(expected), "(", expected, ")",
+	(expectedStats ? " [" + expectedStats + "] " : ""),
+	"!= actual",
+	typeof(actual), "(", actual, ")",
+	(actualStats ? " [" + actualStats + "] " : ""));
 }
 
 function ifInsideLoop()
 {
   var cond = true, intCond = 5, count = 0;
   for (var i = 0; i < 100; i++) {
     if (cond)
       count++;
@@ -1332,23 +1366,16 @@ function testStrict() {
 	a[2] = (n === null);
 	a[3] = (n == null);
     }
     return a.join(",");
 }
 testStrict.expected = "true,false,false,false";
 test(testStrict);
 
-function testGlobalProtoAccess() {
-    return "ok";
-}
-this.__proto__.a = 3; for (var j = 0; j < 4; ++j) { [a]; }
-testGlobalProtoAccess.expected = "ok";
-test(testGlobalProtoAccess);
-
 function testSetPropNeitherMissNorHit() {
     for (var j = 0; j < 5; ++j) { if (({}).__proto__ = 1) { } }
     return "ok";
 }
 testSetPropNeitherMissNorHit.expected = "ok";
 test(testSetPropNeitherMissNorHit);
 
 function testPrimitiveConstructorPrototype() {
@@ -1482,13 +1509,31 @@ function testNestedExitStackOuter() {
   for (var j = 1; j <= RUNLOOP; ++j) {
     for (var k = 1; k <= RUNLOOP; ++k) {
       counter = testNestedExitStackInner(j, counter);
     }
   }
   return counter;
 }
 testNestedExitStackOuter.expected = 81;
+testNestedExitStackOuter.jitstats = {};
+testNestedExitStackOuter.jitstats.recorderStarted = 4;
+testNestedExitStackOuter.jitstats.recorderAborted = 0;
 test(testNestedExitStackOuter);
 
+function testHOTLOOPSize() {
+    return HOTLOOP > 1;
+}
+testHOTLOOPSize.expected = true;
+test(testHOTLOOPSize);
+
+// This test has to come last, since it messes with Object.prototype
+// and thus confuses jitstats.
+function testGlobalProtoAccess() {
+    return "ok";
+}
+this.__proto__.a = 3; for (var j = 0; j < 4; ++j) { [a]; }
+testGlobalProtoAccess.expected = "ok";
+test(testGlobalProtoAccess);
+
 /* Keep these at the end so that we can see the summary after the trace-debug spew. */
 print("\npassed:", passes.length && passes.join(","));
 print("\nFAILED:", fails.length && fails.join(","));