From 9e2a04d5ebe450defe3435d827ef07ca3c0eae3f Mon Sep 17 00:00:00 2001 From: Yury Smolsky Date: Thu, 26 Jul 2018 12:51:06 +0300 Subject: [PATCH] cmd/compile: add sources for inlined functions to ssa.html This CL adds the source code of all inlined functions into the function specified in $GOSSAFUNC. The code is appended to the sources column of ssa.html. ssaDumpInlined is populated with references to inlined functions. Then it is used for dumping the sources in buildssa. The source columns contains code in following order: target function, inlined functions sorted by filename, lineno. Fixes #25904 Change-Id: I4f6d4834376f1efdfda1f968a5335c0543ed36bc Reviewed-on: https://go-review.googlesource.com/126606 Run-TryBot: Yury Smolsky TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- src/cmd/compile/internal/gc/inl.go | 4 ++ src/cmd/compile/internal/gc/ssa.go | 78 ++++++++++++++++++++-------- src/cmd/compile/internal/ssa/html.go | 61 +++++++++++++++++++--- 3 files changed, 114 insertions(+), 29 deletions(-) diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index feb3c8556a..fb5a413b84 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -893,6 +893,10 @@ func mkinlcall1(n, fn *Node, maxCost int32) *Node { fmt.Printf("%v: Before inlining: %+v\n", n.Line(), n) } + if ssaDump != "" && ssaDump == Curfn.funcname() { + ssaDumpInlined = append(ssaDumpInlined, fn) + } + ninit := n.Ninit // Make temp names to use instead of the originals. diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 2abd9448d4..bbd2a668a5 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -27,6 +27,9 @@ var ssaDump string // early copy of $GOSSAFUNC; the func name to dump output var ssaDumpStdout bool // whether to dump to stdout const ssaDumpFile = "ssa.html" +// ssaDumpInlined holds all inlined functions when ssaDump contains a function name. +var ssaDumpInlined []*Node + func initssaconfig() { types_ := ssa.NewTypes() @@ -147,27 +150,7 @@ func buildssa(fn *Node, worker int) *ssa.Func { if printssa { s.f.HTMLWriter = ssa.NewHTMLWriter(ssaDumpFile, 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) - } + dumpSourcesColumn(s.f.HTMLWriter, fn) } // Allocate starting block @@ -239,6 +222,59 @@ func buildssa(fn *Node, worker int) *ssa.Func { return s.f } +func dumpSourcesColumn(writer *ssa.HTMLWriter, fn *Node) { + // Read sources of target function fn. + fname := Ctxt.PosTable.Pos(fn.Pos).Filename() + targetFn, err := readFuncLines(fname, fn.Pos.Line(), fn.Func.Endlineno.Line()) + if err != nil { + writer.Logger.Logf("cannot read sources for function %v: %v", fn, err) + } + + // Read sources of inlined functions. + var inlFns []*ssa.FuncLines + for _, fi := range ssaDumpInlined { + var elno src.XPos + if fi.Name.Defn == nil { + // Endlineno is filled from exported data. + elno = fi.Func.Endlineno + } else { + elno = fi.Name.Defn.Func.Endlineno + } + fname := Ctxt.PosTable.Pos(fi.Pos).Filename() + fnLines, err := readFuncLines(fname, fi.Pos.Line(), elno.Line()) + if err != nil { + writer.Logger.Logf("cannot read sources for function %v: %v", fi, err) + continue + } + inlFns = append(inlFns, fnLines) + } + + sort.Sort(ssa.ByTopo(inlFns)) + if targetFn != nil { + inlFns = append([]*ssa.FuncLines{targetFn}, inlFns...) + } + + writer.WriteSources("sources", inlFns) +} + +func readFuncLines(file string, start, end uint) (*ssa.FuncLines, error) { + f, err := os.Open(os.ExpandEnv(file)) + if err != nil { + return nil, err + } + defer f.Close() + var lines []string + ln := uint(1) + scanner := bufio.NewScanner(f) + for scanner.Scan() && ln <= end { + if ln >= start { + lines = append(lines, scanner.Text()) + } + ln++ + } + return &ssa.FuncLines{Filename: file, StartLineno: start, Lines: lines}, nil +} + // updateUnsetPredPos propagates the earliest-value position information for b // towards all of b's predecessors that need a position, and recurs on that // predecessor if its position is updated. B should have a non-empty position. diff --git a/src/cmd/compile/internal/ssa/html.go b/src/cmd/compile/internal/ssa/html.go index 2e48e8105b..6943e5ef40 100644 --- a/src/cmd/compile/internal/ssa/html.go +++ b/src/cmd/compile/internal/ssa/html.go @@ -458,25 +458,70 @@ func (w *HTMLWriter) WriteFunc(phase, title string, f *Func) { // TODO: Add visual representation of f's CFG. } +// FuncLines contains source code for a function to be displayed +// in sources column. +type FuncLines struct { + Filename string + StartLineno uint + Lines []string +} + +// ByTopo sorts topologically: target function is on top, +// followed by inlined functions sorted by filename and line numbers. +type ByTopo []*FuncLines + +func (x ByTopo) Len() int { return len(x) } +func (x ByTopo) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x ByTopo) Less(i, j int) bool { + a := x[i] + b := x[j] + if a.Filename == a.Filename { + return a.StartLineno < b.StartLineno + } + return a.Filename < b.Filename +} + // 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) { +func (w *HTMLWriter) WriteSources(phase string, all []*FuncLines) { 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) + filename := "" + for _, fl := range all { + fmt.Fprint(&buf, "
 
") + if filename != fl.Filename { + fmt.Fprint(&buf, "
 
") + filename = fl.Filename + } + for i := range fl.Lines { + ln := int(fl.StartLineno) + 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)) + filename = "" + for _, fl := range all { + fmt.Fprint(&buf, "
 
") + if filename != fl.Filename { + fmt.Fprintf(&buf, "
%v
", fl.Filename) + filename = fl.Filename + } + for i, line := range fl.Lines { + ln := int(fl.StartLineno) + i + var escaped string + if strings.TrimSpace(line) == "" { + escaped = " " + } else { + escaped = html.EscapeString(line) + } + fmt.Fprintf(&buf, "
%v
", ln, escaped) + } } fmt.Fprint(&buf, "
") - w.WriteColumn(phase, title, "", buf.String()) + w.WriteColumn(phase, phase, "", buf.String()) } // WriteColumn writes raw HTML in a column headed by title. -- 2.50.0