mirror of https://github.com/golang/go.git
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 <yury@smolsky.by> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
7b8930ed45
commit
8c0425825c
|
|
@ -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("</dt>")
|
||||
buf.WriteString("<dd class=\"ssa-prog\">")
|
||||
buf.WriteString(fmt.Sprintf("%.5d <span class=\"line-number\">(%s)</span> %s", p.Pc, p.InnermostLineNumberHTML(), html.EscapeString(p.InstructionString())))
|
||||
buf.WriteString(fmt.Sprintf("%.5d <span class=\"l%v line-number\">(%s)</span> %s", p.Pc, p.InnermostLineNumber(), p.InnermostLineNumberHTML(), html.EscapeString(p.InstructionString())))
|
||||
buf.WriteString("</dd>")
|
||||
}
|
||||
buf.WriteString("</dl>")
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -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, "<div class=\"lines\" style=\"width: 8%\">")
|
||||
for i, _ := range lines {
|
||||
ln := int(firstLineno) + i
|
||||
fmt.Fprintf(&buf, "<div class=\"l%v line-number\">%v</div>", ln, ln)
|
||||
}
|
||||
fmt.Fprint(&buf, "</div><div style=\"width: 92%\"><pre>")
|
||||
for i, l := range lines {
|
||||
ln := int(firstLineno) + i
|
||||
fmt.Fprintf(&buf, "<div class=\"l%v line-number\">%v</div>", ln, html.EscapeString(l))
|
||||
}
|
||||
fmt.Fprint(&buf, "</pre></div>")
|
||||
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("<span class=\"%s ssa-long-value\">", v.String())
|
||||
|
||||
linenumber := "<span class=\"line-number\">(?)</span>"
|
||||
linenumber := "<span class=\"no-line-number\">(?)</span>"
|
||||
if v.Pos.IsKnown() {
|
||||
linenumber = fmt.Sprintf("<span class=\"line-number\">(%s)</span>", v.Pos.LineNumberHTML())
|
||||
linenumber = fmt.Sprintf("<span class=\"l%v line-number\">(%s)</span>", 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(" <span class=\"l%v line-number\">(%s)</span>", b.Pos.LineNumber(), b.Pos.LineNumberHTML())
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue