Skip to content

Commit 8c04258

Browse files
Yury Smolskybradfitz
Yury Smolsky
authored andcommitted
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent 7b8930e commit 8c04258

File tree

2 files changed

+107
-35
lines changed

2 files changed

+107
-35
lines changed

src/cmd/compile/internal/gc/ssa.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package gc
66

77
import (
8+
"bufio"
89
"bytes"
910
"encoding/binary"
1011
"fmt"
@@ -139,9 +140,30 @@ func buildssa(fn *Node, worker int) *ssa.Func {
139140
s.panics = map[funcLine]*ssa.Block{}
140141
s.softFloat = s.config.SoftFloat
141142

142-
if name == os.Getenv("GOSSAFUNC") {
143+
if printssa {
143144
s.f.HTMLWriter = ssa.NewHTMLWriter("ssa.html", s.f.Frontend(), name)
144145
// TODO: generate and print a mapping from nodes to values and blocks
146+
147+
// Read sources for a function fn and format into a column.
148+
fname := Ctxt.PosTable.Pos(fn.Pos).Filename()
149+
f, err := os.Open(fname)
150+
if err != nil {
151+
s.f.HTMLWriter.Logger.Logf("skipping sources column: %v", err)
152+
} else {
153+
defer f.Close()
154+
firstLn := fn.Pos.Line() - 1
155+
lastLn := fn.Func.Endlineno.Line()
156+
var lines []string
157+
ln := uint(0)
158+
scanner := bufio.NewScanner(f)
159+
for scanner.Scan() && ln < lastLn {
160+
if ln >= firstLn {
161+
lines = append(lines, scanner.Text())
162+
}
163+
ln++
164+
}
165+
s.f.HTMLWriter.WriteSources("sources", fname, firstLn+1, lines)
166+
}
145167
}
146168

147169
// Allocate starting block
@@ -5045,7 +5067,7 @@ func genssa(f *ssa.Func, pp *Progs) {
50455067
}
50465068
buf.WriteString("</dt>")
50475069
buf.WriteString("<dd class=\"ssa-prog\">")
5048-
buf.WriteString(fmt.Sprintf("%.5d <span class=\"line-number\">(%s)</span> %s", p.Pc, p.InnermostLineNumberHTML(), html.EscapeString(p.InstructionString())))
5070+
buf.WriteString(fmt.Sprintf("%.5d <span class=\"l%v line-number\">(%s)</span> %s", p.Pc, p.InnermostLineNumber(), p.InnermostLineNumberHTML(), html.EscapeString(p.InstructionString())))
50495071
buf.WriteString("</dd>")
50505072
}
50515073
buf.WriteString("</dl>")

src/cmd/compile/internal/ssa/html.go

Lines changed: 83 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ body {
5454
}
5555
5656
.stats {
57-
font-size: 60%;
57+
font-size: 60%;
5858
}
5959
6060
table {
@@ -97,6 +97,26 @@ td.collapsed div {
9797
text-align: right;
9898
}
9999
100+
code, pre, .lines {
101+
font-family: Menlo, monospace;
102+
font-size: 12px;
103+
}
104+
105+
.lines {
106+
float: left;
107+
overflow: hidden;
108+
text-align: right;
109+
}
110+
111+
.lines div {
112+
padding-right: 10px;
113+
color: gray;
114+
}
115+
116+
div.line-number {
117+
font-size: 12px;
118+
}
119+
100120
td.ssa-prog {
101121
width: 600px;
102122
word-wrap: break-word;
@@ -158,10 +178,14 @@ dd.ssa-prog {
158178
}
159179
160180
.line-number {
161-
font-style: italic;
162181
font-size: 11px;
163182
}
164183
184+
.no-line-number {
185+
font-size: 11px;
186+
color: gray;
187+
}
188+
165189
.highlight-aquamarine { background-color: aquamarine; }
166190
.highlight-coral { background-color: coral; }
167191
.highlight-lightpink { background-color: lightpink; }
@@ -235,7 +259,7 @@ for (var i = 0; i < outlines.length; i++) {
235259
236260
window.onload = function() {
237261
var ssaElemClicked = function(elem, event, selections, selected) {
238-
event.stopPropagation()
262+
event.stopPropagation();
239263
240264
// TODO: pushState with updated state and read it on page load,
241265
// so that state can survive across reloads
@@ -288,11 +312,11 @@ window.onload = function() {
288312
289313
var ssaValueClicked = function(event) {
290314
ssaElemClicked(this, event, highlights, highlighted);
291-
}
315+
};
292316
293317
var ssaBlockClicked = function(event) {
294318
ssaElemClicked(this, event, outlines, outlined);
295-
}
319+
};
296320
297321
var ssavalues = document.getElementsByClassName("ssa-value");
298322
for (var i = 0; i < ssavalues.length; i++) {
@@ -311,64 +335,68 @@ window.onload = function() {
311335
for (var i = 0; i < ssablocks.length; i++) {
312336
ssablocks[i].addEventListener('click', ssaBlockClicked);
313337
}
314-
var expandedDefault = [
338+
339+
var lines = document.getElementsByClassName("line-number");
340+
for (var i = 0; i < lines.length; i++) {
341+
lines[i].addEventListener('click', ssaValueClicked);
342+
}
343+
344+
// Contains phase names which are expanded by default. Other columns are collapsed.
345+
var expandedDefault = [
315346
"start",
316347
"deadcode",
317348
"opt",
318349
"lower",
319350
"late deadcode",
320351
"regalloc",
321352
"genssa",
322-
]
323-
function isExpDefault(id) {
324-
for (var i = 0; i < expandedDefault.length; i++) {
325-
if (id.startsWith(expandedDefault[i])) {
326-
return true;
327-
}
328-
}
329-
return false;
330-
}
353+
];
354+
331355
function toggler(phase) {
332356
return function() {
333357
toggle_cell(phase+'-col');
334358
toggle_cell(phase+'-exp');
335359
};
336360
}
361+
337362
function toggle_cell(id) {
338-
var e = document.getElementById(id);
339-
if(e.style.display == 'table-cell')
340-
e.style.display = 'none';
341-
else
342-
e.style.display = 'table-cell';
363+
var e = document.getElementById(id);
364+
if (e.style.display == 'table-cell') {
365+
e.style.display = 'none';
366+
} else {
367+
e.style.display = 'table-cell';
368+
}
343369
}
344370
371+
// Go through all columns and collapse needed phases.
345372
var td = document.getElementsByTagName("td");
346373
for (var i = 0; i < td.length; i++) {
347374
var id = td[i].id;
348-
var def = isExpDefault(id);
349375
var phase = id.substr(0, id.length-4);
376+
var show = expandedDefault.indexOf(phase) !== -1
350377
if (id.endsWith("-exp")) {
351378
var h2 = td[i].getElementsByTagName("h2");
352379
if (h2 && h2[0]) {
353380
h2[0].addEventListener('click', toggler(phase));
354381
}
355382
} else {
356-
td[i].addEventListener('click', toggler(phase));
383+
td[i].addEventListener('click', toggler(phase));
357384
}
358-
if (id.endsWith("-col") && def || id.endsWith("-exp") && !def) {
359-
td[i].style.display = 'none';
360-
continue
385+
if (id.endsWith("-col") && show || id.endsWith("-exp") && !show) {
386+
td[i].style.display = 'none';
387+
continue;
361388
}
362389
td[i].style.display = 'table-cell';
363390
}
364391
};
365392
366393
function toggle_visibility(id) {
367-
var e = document.getElementById(id);
368-
if(e.style.display == 'block')
369-
e.style.display = 'none';
370-
else
371-
e.style.display = 'block';
394+
var e = document.getElementById(id);
395+
if (e.style.display == 'block') {
396+
e.style.display = 'none';
397+
} else {
398+
e.style.display = 'block';
399+
}
372400
}
373401
</script>
374402
@@ -414,6 +442,7 @@ func (w *HTMLWriter) Close() {
414442
}
415443

416444
// WriteFunc writes f in a column headed by title.
445+
// phase is used for collapsing columns and should be unique across the table.
417446
func (w *HTMLWriter) WriteFunc(phase, title string, f *Func) {
418447
if w == nil {
419448
return // avoid generating HTML just to discard it
@@ -422,6 +451,27 @@ func (w *HTMLWriter) WriteFunc(phase, title string, f *Func) {
422451
// TODO: Add visual representation of f's CFG.
423452
}
424453

454+
// WriteSources writes lines as source code in a column headed by title.
455+
// phase is used for collapsing columns and should be unique across the table.
456+
func (w *HTMLWriter) WriteSources(phase, title string, firstLineno uint, lines []string) {
457+
if w == nil {
458+
return // avoid generating HTML just to discard it
459+
}
460+
var buf bytes.Buffer
461+
fmt.Fprint(&buf, "<div class=\"lines\" style=\"width: 8%\">")
462+
for i, _ := range lines {
463+
ln := int(firstLineno) + i
464+
fmt.Fprintf(&buf, "<div class=\"l%v line-number\">%v</div>", ln, ln)
465+
}
466+
fmt.Fprint(&buf, "</div><div style=\"width: 92%\"><pre>")
467+
for i, l := range lines {
468+
ln := int(firstLineno) + i
469+
fmt.Fprintf(&buf, "<div class=\"l%v line-number\">%v</div>", ln, html.EscapeString(l))
470+
}
471+
fmt.Fprint(&buf, "</pre></div>")
472+
w.WriteColumn(phase, title, "", buf.String())
473+
}
474+
425475
// WriteColumn writes raw HTML in a column headed by title.
426476
// It is intended for pre- and post-compilation log output.
427477
func (w *HTMLWriter) WriteColumn(phase, title, class, html string) {
@@ -470,9 +520,9 @@ func (v *Value) LongHTML() string {
470520
// maybe we could replace some of that with formatting.
471521
s := fmt.Sprintf("<span class=\"%s ssa-long-value\">", v.String())
472522

473-
linenumber := "<span class=\"line-number\">(?)</span>"
523+
linenumber := "<span class=\"no-line-number\">(?)</span>"
474524
if v.Pos.IsKnown() {
475-
linenumber = fmt.Sprintf("<span class=\"line-number\">(%s)</span>", v.Pos.LineNumberHTML())
525+
linenumber = fmt.Sprintf("<span class=\"l%v line-number\">(%s)</span>", v.Pos.LineNumber(), v.Pos.LineNumberHTML())
476526
}
477527

478528
s += fmt.Sprintf("%s %s = %s", v.HTML(), linenumber, v.Op.String())
@@ -536,7 +586,7 @@ func (b *Block) LongHTML() string {
536586
if b.Pos.IsKnown() {
537587
// TODO does not begin to deal with the full complexity of line numbers.
538588
// Maybe we want a string/slice instead, of outer-inner when inlining.
539-
s += fmt.Sprintf(" (line %s)", b.Pos.LineNumberHTML())
589+
s += fmt.Sprintf(" <span class=\"l%v line-number\">(%s)</span>", b.Pos.LineNumber(), b.Pos.LineNumberHTML())
540590
}
541591
return s
542592
}

0 commit comments

Comments
 (0)