]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: display Go code for a function in ssa.html
authorYury Smolsky <yury@smolsky.by>
Thu, 14 Jun 2018 15:20:03 +0000 (18:20 +0300)
committerBrad Fitzpatrick <bradfitz@golang.org>
Wed, 22 Aug 2018 19:58:07 +0000 (19:58 +0000)
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>
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/ssa/html.go

index 7b254698b793c30f32a65d0f5465043a39e9c1ee..9f9fdc07f8d4ffc7fcb3116cab7c0a594c52b9d0 100644 (file)
@@ -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>")
index 15d64d63e9b03c6a2c60b60a63502e927f066b66..812590934920bb7026c5526844350a2c66fdc631 100644 (file)
@@ -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
 }