Added trace tree visualizer (bug 506714, r=gal).

This commit is contained in:
David Anderson 2009-08-19 16:11:59 -07:00
Родитель 17adceacd3
Коммит 20931663a9
3 изменённых файлов: 342 добавлений и 2 удалений

Просмотреть файл

@ -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

282
js/src/tracevis/tree.py Normal file
Просмотреть файл

@ -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()