summaryrefslogtreecommitdiff
path: root/rts/js/profiling.js
diff options
context:
space:
mode:
Diffstat (limited to 'rts/js/profiling.js')
-rw-r--r--rts/js/profiling.js334
1 files changed, 334 insertions, 0 deletions
diff --git a/rts/js/profiling.js b/rts/js/profiling.js
new file mode 100644
index 0000000000..f972642658
--- /dev/null
+++ b/rts/js/profiling.js
@@ -0,0 +1,334 @@
+//#OPTIONS: CPP
+
+// Used definitions: GHCJS_TRACE_PROF and GHCJS_ASSERT_PROF
+
+#ifdef GHCJS_ASSERT_PROF
+function assert(condition, message) {
+ if (!condition) {
+ console.trace(message || "Assertion failed");
+ }
+}
+#define ASSERT(args...) { assert(args); }
+#else
+#define ASSERT(args...)
+#endif
+
+#ifdef GHCJS_TRACE_PROF
+#define TRACE(args...) { h$log(args); }
+#else
+#define TRACE(args...)
+#endif
+
+/*
+ install the ghcjs-profiling package from /utils/ghcjs-node-profiling
+ to collect cost centre stack information with the node.js profiler
+ */
+var h$registerCC = null, h$registerCCS = null, h$setCCS = null;
+var h$runProf = function(f) {
+ f();
+}
+if(h$isNode()) {
+ (function() {
+ try {
+ var p = require('ghcjs-profiling');
+ if(p.isProfiling()) {
+ h$registerCC = p.registerCC;
+ h$registerCCS = p.registerCCS;
+ h$setCCS = p.setCCS;
+ h$runProf = p.runCC;
+ }
+ } catch(e) {}
+ })();
+}
+
+var h$cachedCurrentCcs = -1;
+function h$reportCurrentCcs() {
+ if(h$setCCS) {
+ if(h$currentThread) {
+ var ccsKey = h$currentThread.ccs._key;
+ if(h$cachedCurrentCcs !== ccsKey) {
+ h$cachedCurrentCcs = ccsKey;
+ h$setCCS(ccsKey);
+ }
+ } else if(h$cachedCurrentCcs !== -1) {
+ h$cachedCurrentCcs = -1;
+ h$setCCS(2147483647); // set to invalid CCS
+ }
+ }
+}
+
+
+var h$ccList = [];
+var h$ccsList = [];
+
+var h$CCUnique = 0;
+/** @constructor */
+function h$CC(label, module, srcloc, isCaf) {
+ //TRACE("h$CC(", label, ", ", module, ", ", srcloc, ", ", isCaf, ")")
+ this.label = label;
+ this.module = module;
+ this.srcloc = srcloc;
+ this.isCaf = isCaf;
+ this._key = h$CCUnique++;
+ this.memAlloc = 0;
+ this.timeTicks = 0;
+ if(h$registerCC) h$registerCC(this._key, label, module + ' ' + srcloc, -1,-1);
+ h$ccList.push(this);
+}
+
+
+var h$CCSUnique = 0;
+/** @constructor */
+function h$CCS(parent, cc) {
+ //TRACE("h$mkCCS(", parent, cc, ")")
+ if (parent !== null && parent.consed.has(cc)) {
+ return (parent.consed.get(cc));
+ }
+ this.consed = new h$Map();
+ this.cc = cc;
+ this._key = h$CCSUnique++;
+ if (parent) {
+ this.root = parent.root;
+ this.depth = parent.depth + 1;
+ this.prevStack = parent;
+ parent.consed.put(cc,this);
+ } else {
+ this.root = this;
+ this.depth = 0;
+ this.prevStack = null;
+ }
+ this.prevStack = parent;
+ this.sccCount = 0;
+ this.timeTicks = 0;
+ this.memAlloc = 0;
+ this.inheritedTicks = 0;
+ this.inheritedAlloc = 0;
+ if(h$registerCCS) {
+ var x = this, stack = [];
+ while(x) { stack.push(x.cc._key); x = x.prevStack; }
+ h$registerCCS(this._key, stack);
+ }
+ h$ccsList.push(this); /* we need all ccs for statistics, not just the root ones */
+}
+
+
+//
+// Built-in cost-centres and stacks
+//
+
+var h$CC_MAIN = new h$CC("MAIN", "MAIN", "<built-in>", false);
+var h$CC_SYSTEM = new h$CC("SYSTEM", "SYSTEM", "<built-in>", false);
+var h$CC_GC = new h$CC("GC", "GC", "<built-in>", false);
+var h$CC_OVERHEAD = new h$CC("OVERHEAD_of", "PROFILING", "<built-in>", false);
+var h$CC_DONT_CARE = new h$CC("DONT_CARE", "MAIN", "<built-in>", false);
+var h$CC_PINNED = new h$CC("PINNED", "SYSTEM", "<built-in>", false);
+var h$CC_IDLE = new h$CC("IDLE", "IDLE", "<built-in>", false);
+var h$CAF_cc = new h$CC("CAF", "CAF", "<built-in>", false);
+
+var h$CCS_MAIN = new h$CCS(null, h$CC_MAIN);
+
+var h$CCS_SYSTEM = new h$CCS(h$CCS_MAIN, h$CC_SYSTEM);
+var h$CCS_GC = new h$CCS(h$CCS_MAIN, h$CC_GC);
+var h$CCS_OVERHEAD = new h$CCS(h$CCS_MAIN, h$CC_OVERHEAD);
+var h$CCS_DONT_CARE = new h$CCS(h$CCS_MAIN, h$CC_DONT_CARE);
+var h$CCS_PINNED = new h$CCS(h$CCS_MAIN, h$CC_PINNED);
+var h$CCS_IDLE = new h$CCS(h$CCS_MAIN, h$CC_IDLE);
+var h$CAF = new h$CCS(h$CCS_MAIN, h$CAF_cc);
+
+
+//
+// Cost-centre entries, SCC
+//
+
+#ifdef GHCJS_TRACE_PROF
+function h$ccsString(ccs) {
+ var labels = [];
+ do {
+ labels.push(ccs.cc.module+'.'+ccs.cc.label+' '+ccs.cc.srcloc);
+ ccs = ccs.prevStack;
+ } while (ccs !== null);
+ return '[' + labels.reverse().join(', ') + ']';
+}
+#endif
+
+function h$pushRestoreCCS() {
+ TRACE("push restoreccs:" + h$ccsString(h$currentThread.ccs))
+ if(h$stack[h$sp] !== h$setCcs_e) {
+ h$sp += 2;
+ h$stack[h$sp-1] = h$currentThread.ccs;
+ h$stack[h$sp] = h$setCcs_e;
+ }
+}
+
+function h$restoreCCS(ccs) {
+ TRACE("restoreccs from:", h$ccsString(h$currentThread.ccs))
+ TRACE(" to:", h$ccsString(ccs))
+ h$currentThread.ccs = ccs;
+ h$reportCurrentCcs();
+}
+
+function h$enterThunkCCS(ccsthunk) {
+ ASSERT(ccsthunk !== null && ccsthunk !== undefined, "ccsthunk is null or undefined")
+ TRACE("entering ccsthunk:", h$ccsString(ccsthunk))
+ h$currentThread.ccs = ccsthunk;
+ h$reportCurrentCcs();
+}
+
+function h$enterFunCCS(ccsapp, // stack at call site
+ ccsfn // stack of function
+ ) {
+ ASSERT(ccsapp !== null && ccsapp !== undefined, "ccsapp is null or undefined")
+ ASSERT(ccsfn !== null && ccsfn !== undefined, "ccsfn is null or undefined")
+ TRACE("ccsapp:", h$ccsString(ccsapp))
+ TRACE("ccsfn:", h$ccsString(ccsfn))
+
+ // common case 1: both stacks are the same
+ if (ccsapp === ccsfn) {
+ return;
+ }
+
+ // common case 2: the function stack is empty, or just CAF
+ if (ccsfn.prevStack === h$CCS_MAIN) {
+ return;
+ }
+
+ // FIXME: do we need this?
+ h$currentThread.ccs = h$CCS_OVERHEAD;
+
+ // common case 3: the stacks are completely different (e.g. one is a
+ // descendent of MAIN and the other of a CAF): we append the whole
+ // of the function stack to the current CCS.
+ if (ccsfn.root !== ccsapp.root) {
+ h$currentThread.ccs = h$appendCCS(ccsapp, ccsfn);
+ h$reportCurrentCcs();
+ return;
+ }
+
+ // uncommon case 4: ccsapp is deeper than ccsfn
+ if (ccsapp.depth > ccsfn.depth) {
+ var tmp = ccsapp;
+ var dif = ccsapp.depth - ccsfn.depth;
+ for (var i = 0; i < dif; i++) {
+ tmp = tmp.prevStack;
+ }
+ h$currentThread.ccs = h$enterFunEqualStacks(ccsapp, tmp, ccsfn);
+ h$reportCurrentCcs();
+ return;
+ }
+
+ // uncommon case 5: ccsfn is deeper than CCCS
+ if (ccsfn.depth > ccsapp.depth) {
+ h$currentThread.ccs = h$enterFunCurShorter(ccsapp, ccsfn, ccsfn.depth - ccsapp.depth);
+ h$reportCurrentCcs();
+ return;
+ }
+
+ // uncommon case 6: stacks are equal depth, but different
+ h$currentThread.ccs = h$enterFunEqualStacks(ccsapp, ccsapp, ccsfn);
+ h$reportCurrentCcs();
+}
+
+function h$appendCCS(ccs1, ccs2) {
+ if (ccs1 === ccs2) {
+ return ccs1;
+ }
+
+ if (ccs2 === h$CCS_MAIN || ccs2.cc.isCaf) {
+ // stop at a CAF element
+ return ccs1;
+ }
+
+ return h$pushCostCentre(h$appendCCS(ccs1, ccs2.prevStack), ccs2.cc);
+}
+
+function h$enterFunCurShorter(ccsapp, ccsfn, n) {
+ if (n === 0) {
+ ASSERT(ccsapp.length === ccsfn.length, "ccsapp.length !== ccsfn.length")
+ return h$enterFunEqualStacks(ccsapp, ccsapp, ccsfn);
+ } else {
+ ASSERT(ccsfn.depth > ccsapp.depth, "ccsfn.depth <= ccsapp.depth")
+ return h$pushCostCentre(h$enterFunCurShorter(ccsapp, ccsfn.prevStack, n-1), ccsfn.cc);
+ }
+}
+
+function h$enterFunEqualStacks(ccs0, ccsapp, ccsfn) {
+ ASSERT(ccsapp.depth === ccsfn.depth, "ccsapp.depth !== ccsfn.depth")
+ if (ccsapp === ccsfn) return ccs0;
+ return h$pushCostCentre(h$enterFunEqualStacks(ccs0, ccsapp.prevStack, ccsfn.prevStack), ccsfn.cc);
+}
+
+function h$pushCostCentre(ccs, cc) {
+ TRACE("pushing cost centre", cc.label, "to", h$ccsString(ccs))
+ if (ccs === null) {
+ // when is ccs null?
+ return new h$CCS(ccs, cc);
+ }
+
+ if (ccs.cc === cc) {
+ return ccs;
+ } else {
+ var temp_ccs = h$checkLoop(ccs, cc);
+ if (temp_ccs !== null) {
+ return temp_ccs;
+ }
+ return new h$CCS(ccs, cc);
+ }
+}
+
+function h$checkLoop(ccs, cc) {
+ while (ccs !== null) {
+ if (ccs.cc === cc)
+ return ccs;
+ ccs = ccs.prevStack;
+ }
+ return null;
+}
+
+//
+// Emulating pointers for cost-centres and cost-centre stacks
+//
+
+var h$ccsCC_offset = 4; // ccs->cc
+var h$ccsPrevStackOffset = 8; // ccs->prevStack
+
+var h$ccLabel_offset = 4; // cc->label
+var h$ccModule_offset = 8; // cc->module
+var h$ccsrcloc_offset = 12; // cc->srcloc
+
+function h$buildCCPtr(o) {
+ // last used offset is 12, so we need to allocate 20 bytes
+ ASSERT(o !== null)
+ var cc = h$newByteArray(20);
+#ifdef GHCJS_TRACE_PROF
+ cc.myTag = "cc pointer";
+#endif
+ cc.arr = [];
+ cc.arr[h$ccLabel_offset] = [h$encodeUtf8(o.label), 0];
+ cc.arr[h$ccModule_offset] = [h$encodeUtf8(o.module), 0];
+ cc.arr[h$ccsrcloc_offset] = [h$encodeUtf8(o.srcloc), 0];
+ return cc;
+}
+
+function h$buildCCSPtr(o) {
+ ASSERT(o !== null)
+ // last used offset is 8, allocate 16 bytes
+ var ccs = h$newByteArray(16);
+#ifdef GHCJS_TRACE_PROF
+ ccs.myTag = "ccs pointer";
+#endif
+ ccs.arr = [];
+ if (o.prevStack !== null) {
+ ccs.arr[h$ccsPrevStackOffset] = [h$buildCCSPtr(o.prevStack), 0];
+ }
+ // FIXME: we may need this part:
+ // else {
+ // ccs.arr[h$ccsPrevStackOffset] = [null, 0];
+ // }
+ ccs.arr[h$ccsCC_offset] = [h$buildCCPtr(o.cc), 0];
+ return ccs;
+}
+
+// run the action with an empty CCS
+function h$clearCCS(a) {
+ throw new Error("ClearCCSOp not implemented");
+}