From 8c0425825ced4042eac0439161abe1f22a47d615 Mon Sep 17 00:00:00 2001 From: Yury Smolsky Date: Thu, 14 Jun 2018 18:20:03 +0300 Subject: [PATCH] cmd/compile: display Go code for a function in ssa.html This CL adds the "sources" column at the beginning of SSA table. This column displays the source code for the function being passed in the GOSSAFUNC env variable. Also UI was extended so that clicking on particular line will highlight all places this line is referenced. JS code was cleaned and formatted. This CL does not handle inlined functions. See issue 25904. Change-Id: Ic7833a0b05e38795f4cf090f3dc82abf62d97026 Reviewed-on: https://go-review.googlesource.com/119035 Run-TryBot: Yury Smolsky TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- src/cmd/compile/internal/gc/ssa.go | 26 +++++- src/cmd/compile/internal/ssa/html.go | 116 +++++++++++++++++++-------- 2 files changed, 107 insertions(+), 35 deletions(-) diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 7b254698b7..9f9fdc07f8 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -5,6 +5,7 @@ package gc import ( + "bufio" "bytes" "encoding/binary" "fmt" @@ -139,9 +140,30 @@ func buildssa(fn *Node, worker int) *ssa.Func { s.panics = map[funcLine]*ssa.Block{} s.softFloat = s.config.SoftFloat - if name == os.Getenv("GOSSAFUNC") { + if printssa { s.f.HTMLWriter = ssa.NewHTMLWriter("ssa.html", s.f.Frontend(), name) // TODO: generate and print a mapping from nodes to values and blocks + + // Read sources for a function fn and format into a column. + fname := Ctxt.PosTable.Pos(fn.Pos).Filename() + f, err := os.Open(fname) + if err != nil { + s.f.HTMLWriter.Logger.Logf("skipping sources column: %v", err) + } else { + defer f.Close() + firstLn := fn.Pos.Line() - 1 + lastLn := fn.Func.Endlineno.Line() + var lines []string + ln := uint(0) + scanner := bufio.NewScanner(f) + for scanner.Scan() && ln < lastLn { + if ln >= firstLn { + lines = append(lines, scanner.Text()) + } + ln++ + } + s.f.HTMLWriter.WriteSources("sources", fname, firstLn+1, lines) + } } // Allocate starting block @@ -5045,7 +5067,7 @@ func genssa(f *ssa.Func, pp *Progs) { } buf.WriteString("") buf.WriteString("
") - buf.WriteString(fmt.Sprintf("%.5d (%s) %s", p.Pc, p.InnermostLineNumberHTML(), html.EscapeString(p.InstructionString()))) + buf.WriteString(fmt.Sprintf("%.5d (%s) %s", p.Pc, p.InnermostLineNumber(), p.InnermostLineNumberHTML(), html.EscapeString(p.InstructionString()))) buf.WriteString("
") } buf.WriteString("") diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go index 15d64d63e9..8125909349 100644 --- a/src/cmd/compile/internal/ssa/html.go +++ b/src/cmd/compile/internal/ssa/html.go @@ -54,7 +54,7 @@ body { } .stats { - font-size: 60%; + font-size: 60%; } table { @@ -97,6 +97,26 @@ td.collapsed div { text-align: right; } +code, pre, .lines { + font-family: Menlo, monospace; + font-size: 12px; +} + +.lines { + float: left; + overflow: hidden; + text-align: right; +} + +.lines div { + padding-right: 10px; + color: gray; +} + +div.line-number { + font-size: 12px; +} + td.ssa-prog { width: 600px; word-wrap: break-word; @@ -158,10 +178,14 @@ dd.ssa-prog { } .line-number { - font-style: italic; font-size: 11px; } +.no-line-number { + font-size: 11px; + color: gray; +} + .highlight-aquamarine { background-color: aquamarine; } .highlight-coral { background-color: coral; } .highlight-lightpink { background-color: lightpink; } @@ -235,7 +259,7 @@ for (var i = 0; i < outlines.length; i++) { window.onload = function() { var ssaElemClicked = function(elem, event, selections, selected) { - event.stopPropagation() + event.stopPropagation(); // TODO: pushState with updated state and read it on page load, // so that state can survive across reloads @@ -288,11 +312,11 @@ window.onload = function() { var ssaValueClicked = function(event) { ssaElemClicked(this, event, highlights, highlighted); - } + }; var ssaBlockClicked = function(event) { ssaElemClicked(this, event, outlines, outlined); - } + }; var ssavalues = document.getElementsByClassName("ssa-value"); for (var i = 0; i < ssavalues.length; i++) { @@ -311,7 +335,14 @@ window.onload = function() { for (var i = 0; i < ssablocks.length; i++) { ssablocks[i].addEventListener('click', ssaBlockClicked); } - var expandedDefault = [ + + var lines = document.getElementsByClassName("line-number"); + for (var i = 0; i < lines.length; i++) { + lines[i].addEventListener('click', ssaValueClicked); + } + + // Contains phase names which are expanded by default. Other columns are collapsed. + var expandedDefault = [ "start", "deadcode", "opt", @@ -319,56 +350,53 @@ window.onload = function() { "late deadcode", "regalloc", "genssa", - ] - function isExpDefault(id) { - for (var i = 0; i < expandedDefault.length; i++) { - if (id.startsWith(expandedDefault[i])) { - return true; - } - } - return false; - } + ]; + function toggler(phase) { return function() { toggle_cell(phase+'-col'); toggle_cell(phase+'-exp'); }; } + function toggle_cell(id) { - var e = document.getElementById(id); - if(e.style.display == 'table-cell') - e.style.display = 'none'; - else - e.style.display = 'table-cell'; + var e = document.getElementById(id); + if (e.style.display == 'table-cell') { + e.style.display = 'none'; + } else { + e.style.display = 'table-cell'; + } } + // Go through all columns and collapse needed phases. var td = document.getElementsByTagName("td"); for (var i = 0; i < td.length; i++) { var id = td[i].id; - var def = isExpDefault(id); var phase = id.substr(0, id.length-4); + var show = expandedDefault.indexOf(phase) !== -1 if (id.endsWith("-exp")) { var h2 = td[i].getElementsByTagName("h2"); if (h2 && h2[0]) { h2[0].addEventListener('click', toggler(phase)); } } else { - td[i].addEventListener('click', toggler(phase)); + td[i].addEventListener('click', toggler(phase)); } - if (id.endsWith("-col") && def || id.endsWith("-exp") && !def) { - td[i].style.display = 'none'; - continue + if (id.endsWith("-col") && show || id.endsWith("-exp") && !show) { + td[i].style.display = 'none'; + continue; } td[i].style.display = 'table-cell'; } }; function toggle_visibility(id) { - var e = document.getElementById(id); - if(e.style.display == 'block') - e.style.display = 'none'; - else - e.style.display = 'block'; + var e = document.getElementById(id); + if (e.style.display == 'block') { + e.style.display = 'none'; + } else { + e.style.display = 'block'; + } } @@ -414,6 +442,7 @@ func (w *HTMLWriter) Close() { } // WriteFunc writes f in a column headed by title. +// phase is used for collapsing columns and should be unique across the table. func (w *HTMLWriter) WriteFunc(phase, title string, f *Func) { if w == nil { return // avoid generating HTML just to discard it @@ -422,6 +451,27 @@ func (w *HTMLWriter) WriteFunc(phase, title string, f *Func) { // TODO: Add visual representation of f's CFG. } +// WriteSources writes lines as source code in a column headed by title. +// phase is used for collapsing columns and should be unique across the table. +func (w *HTMLWriter) WriteSources(phase, title string, firstLineno uint, lines []string) { + if w == nil { + return // avoid generating HTML just to discard it + } + var buf bytes.Buffer + fmt.Fprint(&buf, "
") + for i, _ := range lines { + ln := int(firstLineno) + i + fmt.Fprintf(&buf, "
%v
", ln, ln) + } + fmt.Fprint(&buf, "
")
+	for i, l := range lines {
+		ln := int(firstLineno) + i
+		fmt.Fprintf(&buf, "
%v
", ln, html.EscapeString(l)) + } + fmt.Fprint(&buf, "
") + w.WriteColumn(phase, title, "", buf.String()) +} + // WriteColumn writes raw HTML in a column headed by title. // It is intended for pre- and post-compilation log output. func (w *HTMLWriter) WriteColumn(phase, title, class, html string) { @@ -470,9 +520,9 @@ func (v *Value) LongHTML() string { // maybe we could replace some of that with formatting. s := fmt.Sprintf("", v.String()) - linenumber := "(?)" + linenumber := "(?)" if v.Pos.IsKnown() { - linenumber = fmt.Sprintf("(%s)", v.Pos.LineNumberHTML()) + linenumber = fmt.Sprintf("(%s)", v.Pos.LineNumber(), v.Pos.LineNumberHTML()) } s += fmt.Sprintf("%s %s = %s", v.HTML(), linenumber, v.Op.String()) @@ -536,7 +586,7 @@ func (b *Block) LongHTML() string { if b.Pos.IsKnown() { // TODO does not begin to deal with the full complexity of line numbers. // Maybe we want a string/slice instead, of outer-inner when inlining. - s += fmt.Sprintf(" (line %s)", b.Pos.LineNumberHTML()) + s += fmt.Sprintf(" (%s)", b.Pos.LineNumber(), b.Pos.LineNumberHTML()) } return s } -- 2.50.0