зеркало из https://github.com/mozilla/gecko-dev.git
Added trace tree visualizer (bug 506714, r=gal).
This commit is contained in:
Родитель
17adceacd3
Коммит
20931663a9
|
@ -362,6 +362,7 @@ InitJITLogController()
|
|||
if (strstr(tmf, "abort")) bits |= LC_TMAbort;
|
||||
if (strstr(tmf, "stats")) bits |= LC_TMStats;
|
||||
if (strstr(tmf, "regexp")) bits |= LC_TMRegexp;
|
||||
if (strstr(tmf, "treevis")) bits |= LC_TMTreeVis;
|
||||
|
||||
/* flags for nanojit */
|
||||
if (strstr(tmf, "liveness")) bits |= LC_Liveness;
|
||||
|
@ -400,6 +401,7 @@ InitJITLogController()
|
|||
printf(" abort show trace recording aborts\n");
|
||||
printf(" stats show trace recording stats\n");
|
||||
printf(" regexp show compilation & entry for regexps\n");
|
||||
printf(" treevis spew that tracevis/tree.py can parse\n");
|
||||
printf(" ------ options for Nanojit ------\n");
|
||||
printf(" liveness show LIR liveness at start of rdr pipeline\n");
|
||||
printf(" readlir show LIR as it enters the reader pipeline\n");
|
||||
|
@ -1716,6 +1718,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* _anchor, Fragment* _frag
|
|||
|
||||
debug_only_printf(LC_TMTracer, "globalObj=%p, shape=%d\n",
|
||||
(void*)this->globalObj, OBJ_SHAPE(this->globalObj));
|
||||
debug_only_printf(LC_TMTreeVis, "TREEVIS RECORD FRAG=%p ANCHOR=%p\n", fragment, anchor);
|
||||
|
||||
/* Set up jitstats so that trace-test.js can determine which architecture
|
||||
* we're running on. */
|
||||
|
@ -3223,6 +3226,24 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
#if defined JS_JIT_SPEW
|
||||
JS_REQUIRES_STACK static void
|
||||
TreevisLogExit(JSContext* cx, VMSideExit* exit)
|
||||
{
|
||||
debug_only_printf(LC_TMTreeVis, "TREEVIS ADDEXIT EXIT=%p TYPE=%s FRAG=%p PC=%p FILE=\"%s\""
|
||||
" LINE=%d OFFS=%d", exit, getExitName(exit->exitType), exit->from,
|
||||
cx->fp->regs->pc, cx->fp->script->filename,
|
||||
js_FramePCToLineNumber(cx, cx->fp), FramePCOffset(cx->fp));
|
||||
debug_only_print0(LC_TMTreeVis, " STACK=\"");
|
||||
for (unsigned i = 0; i < exit->numStackSlots; i++)
|
||||
debug_only_printf(LC_TMTreeVis, "%c", typeChar[exit->stackTypeMap()[i]]);
|
||||
debug_only_print0(LC_TMTreeVis, "\" GLOBALS=\"");
|
||||
for (unsigned i = 0; i < exit->numGlobalSlots; i++)
|
||||
debug_only_printf(LC_TMTreeVis, "%c", typeChar[exit->globalTypeMap()[i]]);
|
||||
debug_only_print0(LC_TMTreeVis, "\"\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
JS_REQUIRES_STACK VMSideExit*
|
||||
TraceRecorder::snapshot(ExitType exitType)
|
||||
{
|
||||
|
@ -3326,6 +3347,9 @@ TraceRecorder::snapshot(ExitType exitType)
|
|||
ngslots == e->numGlobalSlots &&
|
||||
!memcmp(exits[n]->fullTypeMap(), typemap, typemap_size)) {
|
||||
AUDIT(mergedLoopExits);
|
||||
#if defined JS_JIT_SPEW
|
||||
TreevisLogExit(cx, e);
|
||||
#endif
|
||||
JS_ARENA_RELEASE(&cx->tempPool, mark);
|
||||
return e;
|
||||
}
|
||||
|
@ -3373,6 +3397,10 @@ TraceRecorder::snapshot(ExitType exitType)
|
|||
exit->lookupFlags = js_InferFlags(cx, 0);
|
||||
memcpy(exit->fullTypeMap(), typemap, typemap_size);
|
||||
|
||||
#if defined JS_JIT_SPEW
|
||||
TreevisLogExit(cx, exit);
|
||||
#endif
|
||||
|
||||
JS_ARENA_RELEASE(&cx->tempPool, mark);
|
||||
return exit;
|
||||
}
|
||||
|
@ -3447,6 +3475,7 @@ TraceRecorder::copy(VMSideExit* copy)
|
|||
*/
|
||||
if (exit->exitType == LOOP_EXIT)
|
||||
treeInfo->sideExits.add(exit);
|
||||
TreevisLogExit(cx, exit);
|
||||
return exit;
|
||||
}
|
||||
|
||||
|
@ -3576,6 +3605,8 @@ JoinPeers(Assembler* assm, VMSideExit* exit, VMFragment* target)
|
|||
exit->target = target;
|
||||
assm->patch(exit);
|
||||
|
||||
debug_only_printf(LC_TMTreeVis, "TREEVIS JOIN ANCHOR=%p FRAG=%p\n", exit, target);
|
||||
|
||||
if (exit->root() == target)
|
||||
return;
|
||||
|
||||
|
@ -3898,6 +3929,9 @@ TraceRecorder::closeLoop(SlotMap& slotMap, VMSideExit* exit, TypeConsensus& cons
|
|||
treeInfo->linkedTrees.addUnique(peer);
|
||||
}
|
||||
} else {
|
||||
exit->exitType = LOOP_EXIT;
|
||||
debug_only_printf(LC_TMTreeVis, "TREEVIS CHANGEEXIT EXIT=%p TYPE=%s\n", exit,
|
||||
getExitName(LOOP_EXIT));
|
||||
exit->target = fragment->root;
|
||||
fragment->lastIns = lir->insGuard(LIR_loop, lir->insImm(1), createGuardRecord(exit));
|
||||
}
|
||||
|
@ -3907,6 +3941,8 @@ TraceRecorder::closeLoop(SlotMap& slotMap, VMSideExit* exit, TypeConsensus& cons
|
|||
if (assm->error() != nanojit::None)
|
||||
return false;
|
||||
|
||||
debug_only_printf(LC_TMTreeVis, "TREEVIS CLOSELOOP EXIT=%p PEER=%p\n", exit, peer);
|
||||
|
||||
peer = getLoop(traceMonitor, root->ip, root->globalObj, root->globalShape, root->argc);
|
||||
JS_ASSERT(peer);
|
||||
joinEdgesToEntry(fragmento, peer);
|
||||
|
@ -4055,6 +4091,8 @@ TraceRecorder::endLoop(VMSideExit* exit)
|
|||
if (assm->error() != nanojit::None)
|
||||
return;
|
||||
|
||||
debug_only_printf(LC_TMTreeVis, "TREEVIS ENDLOOP EXIT=%p\n", exit);
|
||||
|
||||
VMFragment* root = (VMFragment*)fragment->root;
|
||||
joinEdgesToEntry(traceMonitor->fragmento, getLoop(traceMonitor,
|
||||
root->ip,
|
||||
|
@ -4189,7 +4227,10 @@ TraceRecorder::emitTreeCall(Fragment* inner, VMSideExit* exit)
|
|||
* Guard that we come out of the inner tree along the same side exit we came out when
|
||||
* we called the inner tree at recording time.
|
||||
*/
|
||||
guard(true, lir->ins2(LIR_eq, ret, INS_CONSTPTR(exit)), NESTED_EXIT);
|
||||
VMSideExit* nested = snapshot(NESTED_EXIT);
|
||||
guard(true, lir->ins2(LIR_eq, ret, INS_CONSTPTR(exit)), nested);
|
||||
debug_only_printf(LC_TMTreeVis, "TREEVIS TREECALL INNER=%p EXIT=%p GUARD=%p\n", inner, nested,
|
||||
exit);
|
||||
|
||||
/* Register us as a dependent tree of the inner tree. */
|
||||
((TreeInfo*)inner->vmprivate)->dependentTrees.addUnique(fragment->root);
|
||||
|
@ -4491,6 +4532,7 @@ TrashTree(JSContext* cx, Fragment* f)
|
|||
{
|
||||
JS_ASSERT((!f->code()) == (!f->vmprivate));
|
||||
JS_ASSERT(f == f->root);
|
||||
debug_only_printf(LC_TMTreeVis, "TREEVIS TRASH FRAG=%p\n", f);
|
||||
if (!f->code())
|
||||
return;
|
||||
AUDIT(treesTrashed);
|
||||
|
@ -4772,6 +4814,17 @@ RecordTree(JSContext* cx, JSTraceMonitor* tm, Fragment* f, jsbytecode* outer,
|
|||
ti->treeLineNumber = js_FramePCToLineNumber(cx, cx->fp);
|
||||
ti->treePCOffset = FramePCOffset(cx->fp);
|
||||
#endif
|
||||
#ifdef JS_JIT_SPEW
|
||||
debug_only_printf(LC_TMTreeVis, "TREEVIS CREATETREE ROOT=%p PC=%p FILE=\"%s\" LINE=%d OFFS=%d",
|
||||
f, f->ip, ti->treeFileName, ti->treeLineNumber, FramePCOffset(cx->fp));
|
||||
debug_only_print0(LC_TMTreeVis, " STACK=\"");
|
||||
for (unsigned i = 0; i < ti->nStackTypes; i++)
|
||||
debug_only_printf(LC_TMTreeVis, "%c", typeChar[ti->typeMap[i]]);
|
||||
debug_only_print0(LC_TMTreeVis, "\" GLOBALS=\"");
|
||||
for (unsigned i = 0; i < ti->nGlobalTypes(); i++)
|
||||
debug_only_printf(LC_TMTreeVis, "%c", typeChar[ti->typeMap[ti->nStackTypes + i]]);
|
||||
debug_only_print0(LC_TMTreeVis, "\"\n");
|
||||
#endif
|
||||
|
||||
/* Determine the native frame layout at the entry point. */
|
||||
unsigned entryNativeStackSlots = ti->nStackTypes;
|
||||
|
@ -4921,6 +4974,10 @@ AttemptToExtendTree(JSContext* cx, VMSideExit* anchor, VMSideExit* exitedFrom, j
|
|||
Fragment* c;
|
||||
if (!(c = anchor->target)) {
|
||||
c = JS_TRACE_MONITOR(cx).fragmento->createBranch(anchor, cx->fp->regs->pc);
|
||||
debug_only_printf(LC_TMTreeVis, "TREEVIS CREATEBRANCH ROOT=%p FRAG=%p PC=%p FILE=\"%s\""
|
||||
" LINE=%d ANCHOR=%p OFFS=%d\n",
|
||||
f, c, cx->fp->regs->pc, cx->fp->script->filename,
|
||||
js_FramePCToLineNumber(cx, cx->fp), anchor, FramePCOffset(cx->fp));
|
||||
c->spawnedFrom = anchor;
|
||||
c->parent = f;
|
||||
anchor->target = c;
|
||||
|
|
|
@ -185,7 +185,8 @@ enum LC_TMBits {
|
|||
LC_TMPatcher = 1<<19,
|
||||
LC_TMAbort = 1<<20,
|
||||
LC_TMStats = 1<<21,
|
||||
LC_TMRegexp = 1<<22
|
||||
LC_TMRegexp = 1<<22,
|
||||
LC_TMTreeVis = 1<<23
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,282 @@
|
|||
#!/usr/bin/python
|
||||
# vim: set ts=2 sw=2 tw=99 noet:
|
||||
import re
|
||||
import sys
|
||||
import string
|
||||
|
||||
class Node:
|
||||
def __init__(self, address):
|
||||
self.address = address
|
||||
|
||||
class Fragment:
|
||||
def __init__(self, address, root, pc, file, line, offset):
|
||||
self.address = 'x' + address[2:]
|
||||
if root == None:
|
||||
self.root = self
|
||||
else:
|
||||
self.root = root
|
||||
self.pc = pc
|
||||
self.file = file
|
||||
self.line = line
|
||||
self.exits = []
|
||||
self.marked = False
|
||||
self.offset = offset
|
||||
self.stackTypes = None
|
||||
self.globalTypes = None
|
||||
self.lastExit = None
|
||||
self.nodes = {}
|
||||
|
||||
def addNode(self, exit):
|
||||
node = Node('L' + self.address + 'E' + exit.address)
|
||||
self.nodes[exit.address] = node
|
||||
return node
|
||||
|
||||
def nodeName(self, exit):
|
||||
return self.nodes[exit.address].address
|
||||
|
||||
def type(self):
|
||||
if self == self.root:
|
||||
return 'loop'
|
||||
else:
|
||||
return 'branch'
|
||||
|
||||
def describe(self):
|
||||
if self.stackTypes == None:
|
||||
return '<' + self.type() + '<br/>' + self.line + '@' + self.offset + \
|
||||
'<br/>' + self.address + '>'
|
||||
else:
|
||||
return '<' + self.type() + '<br/>' + self.line + '@' + self.offset + \
|
||||
'<br/>' + self.address + ', ' + self.stackTypes + ',' + self.globalTypes + '>'
|
||||
|
||||
def addExit(self, e):
|
||||
e.owner = self
|
||||
self.exits.append(e)
|
||||
|
||||
class Exit:
|
||||
def __init__(self, address, type, pc, file, line, offs, _s, _g):
|
||||
self.address = 'x' + address[2:]
|
||||
self.type = type
|
||||
self.pc = pc
|
||||
self.file = file
|
||||
self.line = line
|
||||
self.offset = offs
|
||||
self.stackTypes = _s
|
||||
self.globalTypes = _g
|
||||
self.owner = None
|
||||
self.target = None
|
||||
self.treeGuard = None
|
||||
self.marked = False
|
||||
|
||||
def describe(self):
|
||||
return '<' + self.type + '<br/>' + self.address + ', line ' + self.line + '@' + self.offset + \
|
||||
'<br/>' + self.stackTypes + ',' + self.globalTypes + '>'
|
||||
|
||||
class TraceRecorder:
|
||||
def __init__(self):
|
||||
self.trees = {}
|
||||
self.fragments = {}
|
||||
self.exits = {}
|
||||
self.currentFrag = None
|
||||
self.currentAnchor = None
|
||||
self.edges = []
|
||||
self.treeEdges = []
|
||||
|
||||
def createTree(self, ROOT, PC, FILE, LINE, STACK, GLOBALS, OFFS):
|
||||
f = Fragment(ROOT, None, PC, FILE, LINE, OFFS)
|
||||
f.stackTypes = STACK
|
||||
f.globalTypes = GLOBALS
|
||||
self.trees[ROOT] = f
|
||||
self.fragments[ROOT] = f
|
||||
return f
|
||||
|
||||
def createBranch(self, ROOT, FRAG, PC, FILE, LINE, ANCHOR, OFFS):
|
||||
root = self.trees[ROOT]
|
||||
branch = Fragment(FRAG, root, PC, FILE, LINE, OFFS)
|
||||
self.fragments[FRAG] = branch
|
||||
return branch
|
||||
|
||||
def record(self, FRAG, ANCHOR):
|
||||
self.currentFrag = self.fragments[FRAG]
|
||||
if self.currentFrag.root != self.currentFrag:
|
||||
self.currentAnchor = self.exits[ANCHOR]
|
||||
else:
|
||||
self.currentAnchor = None
|
||||
|
||||
def addExit(self, EXIT, TYPE, FRAG, PC, FILE, LINE, OFFS, STACK, GLOBALS):
|
||||
if EXIT not in self.exits:
|
||||
e = Exit(EXIT, TYPE, PC, FILE, LINE, OFFS, STACK, GLOBALS)
|
||||
e.owner = self.fragments[FRAG]
|
||||
self.exits[EXIT] = e
|
||||
else:
|
||||
e = self.exits[EXIT]
|
||||
self.currentFrag.addExit(e)
|
||||
|
||||
def changeExit(self, EXIT, TYPE):
|
||||
exit = self.exits[EXIT]
|
||||
exit.type = TYPE
|
||||
|
||||
def endLoop(self, EXIT):
|
||||
exit = self.exits[EXIT]
|
||||
if self.currentAnchor != None:
|
||||
self.currentAnchor.target = self.currentFrag
|
||||
self.currentFrag.lastExit = exit
|
||||
self.currentFrag = None
|
||||
self.currentAnchor = None
|
||||
|
||||
def closeLoop(self, EXIT, PEER):
|
||||
exit = self.exits[EXIT]
|
||||
if self.currentAnchor != None:
|
||||
self.currentAnchor.target = self.currentFrag
|
||||
if exit.type != "LOOP" and PEER != "0x0":
|
||||
peer = self.trees[PEER]
|
||||
exit.target = peer
|
||||
self.currentFrag.lastExit = exit
|
||||
self.currentFrag = None
|
||||
self.currentAnchor = None
|
||||
|
||||
def join(self, ANCHOR, FRAG):
|
||||
exit = self.exits[ANCHOR]
|
||||
fragment = self.fragments[FRAG]
|
||||
exit.target = fragment
|
||||
|
||||
def treeCall(self, INNER, EXIT, GUARD):
|
||||
exit = self.exits[EXIT]
|
||||
inner = self.trees[INNER]
|
||||
guard = self.exits[GUARD]
|
||||
exit.target = inner
|
||||
exit.treeGuard = guard
|
||||
|
||||
def trash(self, FRAG):
|
||||
pass
|
||||
|
||||
def readLine(self, line):
|
||||
cmd = re.match(r"TREEVIS ([^ ]+) (.*)$", line)
|
||||
if cmd == None:
|
||||
return
|
||||
cmd = cmd.groups()
|
||||
keys = dict([w.split('=') for w in string.split(cmd[1])])
|
||||
if cmd[0] == "CREATETREE":
|
||||
self.createTree(**keys)
|
||||
elif cmd[0] == "RECORD":
|
||||
self.record(**keys)
|
||||
elif cmd[0] == "ADDEXIT":
|
||||
self.addExit(**keys)
|
||||
elif cmd[0] == "CREATEBRANCH":
|
||||
self.createBranch(**keys)
|
||||
elif cmd[0] == "CLOSELOOP":
|
||||
self.closeLoop(**keys)
|
||||
elif cmd[0] == "CHANGEEXIT":
|
||||
self.changeExit(**keys)
|
||||
elif cmd[0] == "ENDLOOP":
|
||||
self.endLoop(**keys)
|
||||
elif cmd[0] == "JOIN":
|
||||
self.join(**keys)
|
||||
elif cmd[0] == "TRASH":
|
||||
self.trash(**keys)
|
||||
elif cmd[0] == "TREECALL":
|
||||
self.treeCall(**keys)
|
||||
else:
|
||||
raise Exception("unknown command: " + cmd[0])
|
||||
|
||||
def describeTree(self, fragment):
|
||||
return '<' + 'tree call to<br/>' + fragment.line + '@' + fragment.offset + '>'
|
||||
|
||||
def emitExit(self, fragment, exit):
|
||||
if exit.type == "NESTED":
|
||||
print '\t\ttc_' + exit.address + ' [ shape = octagon, label = ' + \
|
||||
self.describeTree(exit.target) + ' ]'
|
||||
self.edges.append(['tc_' + exit.address, exit.target.address])
|
||||
node = fragment.addNode(exit)
|
||||
print '\t\t' + node.address + ' [ label = ' + exit.describe() + ' ]'
|
||||
|
||||
def emitTrace(self, fragment):
|
||||
if fragment.marked == True:
|
||||
raise Exception('fragment already marked')
|
||||
print "\t\t" + fragment.address + " [ label = <start> ] "
|
||||
for e in fragment.exits:
|
||||
if e.target == None and (e != fragment.lastExit and e.type != 'LOOP'):
|
||||
continue
|
||||
self.emitExit(fragment, e)
|
||||
last = fragment.address
|
||||
for e in fragment.exits:
|
||||
if e.target == None and (e != fragment.lastExit and e.type != 'LOOP'):
|
||||
continue
|
||||
if e.type == "NESTED":
|
||||
print '\t\t' + last + ' -> tc_' + e.address
|
||||
print '\t\ttc_' + e.address + ' -> ' + fragment.nodeName(e) + \
|
||||
' [ arrowtail = dot, arrowhead = onormal ] '
|
||||
if e.treeGuard != None:
|
||||
self.treeEdges.append([e, fragment.nodeName(e)])
|
||||
else:
|
||||
print '\t\t' + last + ' -> ' + fragment.nodeName(e)
|
||||
if e.target == None and e.type == 'LOOP':
|
||||
self.edges.append([fragment.nodeName(e), fragment.root.address])
|
||||
elif e.target != None:
|
||||
self.edges.append([fragment.nodeName(e), e.target.address])
|
||||
last = fragment.nodeName(e)
|
||||
|
||||
def emitBranch(self, fragment):
|
||||
if fragment.root == fragment:
|
||||
raise Exception('branches have root!=frag')
|
||||
if fragment.lastExit == None:
|
||||
fragment.marked = True
|
||||
return
|
||||
print '\tsubgraph cluster_' + fragment.address + ' {'
|
||||
print '\t\tlabel = ' + fragment.describe()
|
||||
self.emitTrace(fragment)
|
||||
fragment.marked = True
|
||||
print "\t}"
|
||||
self.emitBranches(fragment)
|
||||
|
||||
def emitBranches(self, fragment):
|
||||
for e in fragment.exits:
|
||||
if not e.target or e.target.marked:
|
||||
continue
|
||||
if e.target == e.target.root:
|
||||
self.emitTree(e.target)
|
||||
else:
|
||||
self.emitBranch(e.target)
|
||||
|
||||
def emitTree(self, fragment):
|
||||
if fragment.root != fragment:
|
||||
raise Exception('trees have root=frag')
|
||||
if fragment.lastExit == None:
|
||||
fragment.marked = True
|
||||
return
|
||||
print '\tsubgraph cluster_' + fragment.address + ' {'
|
||||
print '\t\tlabel = ' + fragment.describe()
|
||||
self.emitTrace(fragment)
|
||||
print "\t}"
|
||||
fragment.marked = True
|
||||
self.emitBranches(fragment)
|
||||
|
||||
def emitGraph(self):
|
||||
print "digraph G {"
|
||||
worklist = [self.trees[fragment] for fragment in self.trees]
|
||||
for fragment in worklist:
|
||||
if fragment.marked:
|
||||
continue
|
||||
self.emitTree(fragment)
|
||||
for edge in self.edges:
|
||||
name = edge[0]
|
||||
address = edge[1]
|
||||
print '\t' + name + ' -> ' + address
|
||||
for edge in self.treeEdges:
|
||||
fromName = edge[0].treeGuard.owner.nodeName(edge[0].treeGuard)
|
||||
toName = edge[1]
|
||||
print '\t' + fromName + ' -> ' + toName + \
|
||||
' [ arrowtail = dot ]'
|
||||
print "}"
|
||||
|
||||
def fromFile(self, file):
|
||||
line = file.readline()
|
||||
while line != "":
|
||||
self.readLine(line)
|
||||
line = file.readline()
|
||||
|
||||
tr = TraceRecorder()
|
||||
f = open(sys.argv[1], "r")
|
||||
tr.fromFile(f)
|
||||
f.close()
|
||||
tr.emitGraph()
|
||||
|
Загрузка…
Ссылка в новой задаче