From edcf2d0cd604062cdd3b28d7e679b8b271ddc2ec Mon Sep 17 00:00:00 2001 From: David Chase Date: Tue, 10 Oct 2017 14:44:15 -0400 Subject: [PATCH] cmd/compile: add line numbers to values & blocks in ssa.html MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit In order to improve the line numbering for debuggers, it's necessary to trace lines through compilation. This makes it (much) easier to follow. The format of the last column of the ssa.html output was also changed to reduce the spamminess of the file name, which is usually the same and makes it far harder to read instructions and line numbers, and to make it wider and also able to break words when wrapping (long path names still can push off the end otherwise; side-to-side scrolling was tried but was more annoying than the occasional wrapped line). Sample output now, where [...] is elision for sake of making the CL character-counter happy -- and the (##) line numbers are rendered in italics and a smaller font (11 point) under control of a CSS class "line-number". genssa # /Users/drchase/[...]/ssa/testdata/hist.go 00000 (35) TEXT "".main(SB) 00001 (35) FUNCDATA $0, gclocals·7be4bb[...]1e8b(SB) 00002 (35) FUNCDATA $1, gclocals·9ab98a[...]4568(SB) v920 00003 (36) LEAQ ""..autotmp_31-640(SP), DI v858 00004 (36) XORPS X0, X0 v6 00005 (36) LEAQ -48(DI), DI v6 00006 (36) DUFFZERO $277 v576 00007 (36) LEAQ ""..autotmp_31-640(SP), AX v10 00008 (36) TESTB AX, (AX) b1 00009 (36) JMP 10 and from an earlier phase: b18: ← b17 v242 (47) = Copy v238 v243 (47) = VarKill {.autotmp_16} v242 v244 (48) = Addr <**bufio.Scanner> {scanner} v2 v245 (48) = Load <*bufio.Scanner> v244 v243 [...] v279 (49) = Store {int64} v277 v276 v278 v280 (49) = Addr <*error> {.autotmp_18} v2 v281 (49) = Load v280 v279 v282 (49) = Addr <*error> {err} v2 v283 (49) = VarDef {err} v279 v284 (49) = Store {error} v282 v281 v283 v285 (47) = VarKill {.autotmp_18} v284 v286 (47) = VarKill {.autotmp_17} v285 v287 (50) = Addr <*error> {err} v2 v288 (50) = Load v287 v286 v289 (50) = NeqInter v288 v51 If v289 → b21 b22 (line 50) Change-Id: I3f46310918f965761f59e6f03ea53067237c28a8 Reviewed-on: https://go-review.googlesource.com/69591 Run-TryBot: David Chase TryBot-Result: Gobot Gobot Reviewed-by: Cherry Zhang --- src/cmd/compile/fmt_test.go | 3 ++- src/cmd/compile/internal/gc/ssa.go | 17 ++++++++++--- src/cmd/compile/internal/ssa/html.go | 35 ++++++++++++++++++++++--- src/cmd/internal/obj/util.go | 38 +++++++++++++++++++++++++++- 4 files changed, 83 insertions(+), 10 deletions(-) diff --git a/src/cmd/compile/fmt_test.go b/src/cmd/compile/fmt_test.go index 6d18d495c3..17716f82f2 100644 --- a/src/cmd/compile/fmt_test.go +++ b/src/cmd/compile/fmt_test.go @@ -229,7 +229,7 @@ func TestFormats(t *testing.T) { } } if mismatch { - t.Errorf("knownFormats is out of date; please run with -v to regenerate") + t.Errorf("knownFormats is out of date; please 'go test -v fmt_test.go > foo', then extract new definition of knownFormats from foo") } } @@ -683,6 +683,7 @@ var knownFormats = map[string]string{ "int32 %d": "", "int32 %v": "", "int32 %x": "", + "int64 %.5d": "", "int64 %+d": "", "int64 %-10d": "", "int64 %X": "", diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index c633ee4c93..69ed613412 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4606,22 +4606,31 @@ func genssa(f *ssa.Func, pp *Progs) { var buf bytes.Buffer buf.WriteString("") buf.WriteString("
") + filename := "" for p := pp.Text; p != nil; p = p.Link { + // Don't spam every line with the file name, which is often huge. + // Only print changes. + if f := p.FileName(); f != filename { + filename = f + buf.WriteString("
") + buf.WriteString(html.EscapeString("# " + filename)) + buf.WriteString("
") + } + buf.WriteString("
") if v, ok := progToValue[p]; ok { buf.WriteString(v.HTML()) } else if b, ok := progToBlock[p]; ok { - buf.WriteString(b.HTML()) + buf.WriteString("" + b.HTML() + "") } buf.WriteString("
") buf.WriteString("
") - buf.WriteString(html.EscapeString(p.String())) + buf.WriteString(fmt.Sprintf("%.5d (%s) %s", p.Pc, p.LineNumber(), html.EscapeString(p.InstructionString()))) buf.WriteString("
") - buf.WriteString("") } buf.WriteString("
") buf.WriteString("
") - f.HTMLWriter.WriteColumn("genssa", buf.String()) + f.HTMLWriter.WriteColumn("genssa", "ssa-prog", buf.String()) // pp.Text.Ctxt.LineHist.PrintFilenameOnly = saved } } diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go index bb87378bdd..47f37f2337 100644 --- a/src/cmd/compile/internal/ssa/html.go +++ b/src/cmd/compile/internal/ssa/html.go @@ -66,6 +66,11 @@ th, td { padding: 5px; } +td.ssa-prog { + width: 600px; + word-wrap: break-word; +} + li { list-style-type: none; } @@ -121,6 +126,11 @@ dd.ssa-prog { font-style: italic; } +.line-number { + font-style: italic; + font-size: 11px; +} + .highlight-yellow { background-color: yellow; } .highlight-aquamarine { background-color: aquamarine; } .highlight-coral { background-color: coral; } @@ -310,17 +320,21 @@ func (w *HTMLWriter) WriteFunc(title string, f *Func) { if w == nil { return // avoid generating HTML just to discard it } - w.WriteColumn(title, f.HTML()) + w.WriteColumn(title, "", f.HTML()) // TODO: Add visual representation of f's CFG. } // WriteColumn writes raw HTML in a column headed by title. // It is intended for pre- and post-compilation log output. -func (w *HTMLWriter) WriteColumn(title string, html string) { +func (w *HTMLWriter) WriteColumn(title, class, html string) { if w == nil { return } - w.WriteString("") + if class == "" { + w.WriteString("") + } else { + w.WriteString("") + } w.WriteString("

" + title + "

") w.WriteString(html) w.WriteString("") @@ -353,7 +367,14 @@ func (v *Value) LongHTML() string { // We already have visual noise in the form of punctuation // maybe we could replace some of that with formatting. s := fmt.Sprintf("", v.String()) - s += fmt.Sprintf("%s = %s", v.HTML(), v.Op.String()) + + linenumber := "(?)" + if v.Pos.IsKnown() { + linenumber = fmt.Sprintf("(%d)", v.Pos.Line()) + } + + s += fmt.Sprintf("%s %s = %s", v.HTML(), linenumber, v.Op.String()) + s += " <" + html.EscapeString(v.Type.String()) + ">" s += html.EscapeString(v.auxString()) for _, a := range v.Args { @@ -375,6 +396,7 @@ func (v *Value) LongHTML() string { if len(names) != 0 { s += " (" + strings.Join(names, ", ") + ")" } + s += "" return s } @@ -409,6 +431,11 @@ func (b *Block) LongHTML() string { case BranchLikely: s += " (likely)" } + 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 %d)", b.Pos.Line()) + } return s } diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go index bf2d209d7f..67c74c2f89 100644 --- a/src/cmd/internal/obj/util.go +++ b/src/cmd/internal/obj/util.go @@ -13,10 +13,34 @@ import ( const REG_NONE = 0 +// Line returns a string containing the filename and line number for p func (p *Prog) Line() string { return p.Ctxt.OutermostPos(p.Pos).Format(false) } +// LineNumber returns a string containing the line number for p's position +func (p *Prog) LineNumber() string { + pos := p.Ctxt.OutermostPos(p.Pos) + if !pos.IsKnown() { + return "?" + } + return fmt.Sprintf("%d", pos.Line()) +} + +// FileName returns a string containing the filename for p's position +func (p *Prog) FileName() string { + // TODO LineNumber and FileName cases don't handle full generality of positions, + // but because these are currently used only for GOSSAFUNC debugging output, that + // is okay. The intent is that "LineNumber()" yields the rapidly varying part, + // while "FileName()" yields the longer and slightly more constant material. + pos := p.Ctxt.OutermostPos(p.Pos) + if !pos.IsKnown() { + return "" + } + + return pos.Filename() +} + var armCondCode = []string{ ".EQ", ".NE", @@ -72,6 +96,18 @@ func (p *Prog) String() string { if p == nil { return "" } + if p.Ctxt == nil { + return "" + } + return fmt.Sprintf("%.5d (%v)\t%s", p.Pc, p.Line(), p.InstructionString()) +} + +// InstructionString returns a string representation of the instruction without preceding +// program counter or file and line number. +func (p *Prog) InstructionString() string { + if p == nil { + return "" + } if p.Ctxt == nil { return "" @@ -81,7 +117,7 @@ func (p *Prog) String() string { var buf bytes.Buffer - fmt.Fprintf(&buf, "%.5d (%v)\t%v%s", p.Pc, p.Line(), p.As, sc) + fmt.Fprintf(&buf, "%v%s", p.As, sc) sep := "\t" if p.From.Type != TYPE_NONE { -- 2.48.1