github.com/mattn/go@v0.0.0-20171011075504-07f7db3ea99f/src/cmd/compile/internal/ssa/html.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ssa
     6  
     7  import (
     8  	"bytes"
     9  	"cmd/internal/src"
    10  	"fmt"
    11  	"html"
    12  	"io"
    13  	"os"
    14  	"strings"
    15  )
    16  
    17  type HTMLWriter struct {
    18  	Logger
    19  	w io.WriteCloser
    20  }
    21  
    22  func NewHTMLWriter(path string, logger Logger, funcname string) *HTMLWriter {
    23  	out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
    24  	if err != nil {
    25  		logger.Fatalf(src.NoXPos, "%v", err)
    26  	}
    27  	html := HTMLWriter{w: out, Logger: logger}
    28  	html.start(funcname)
    29  	return &html
    30  }
    31  
    32  func (w *HTMLWriter) start(name string) {
    33  	if w == nil {
    34  		return
    35  	}
    36  	w.WriteString("<html>")
    37  	w.WriteString(`<head>
    38  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    39  <style>
    40  
    41  #helplink {
    42      margin-bottom: 15px;
    43      display: block;
    44      margin-top: -15px;
    45  }
    46  
    47  #help {
    48      display: none;
    49  }
    50  
    51  .stats {
    52  	font-size: 60%;
    53  }
    54  
    55  table {
    56      border: 1px solid black;
    57      table-layout: fixed;
    58      width: 300px;
    59  }
    60  
    61  th, td {
    62      border: 1px solid black;
    63      overflow: hidden;
    64      width: 400px;
    65      vertical-align: top;
    66      padding: 5px;
    67  }
    68  
    69  li {
    70      list-style-type: none;
    71  }
    72  
    73  li.ssa-long-value {
    74      text-indent: -2em;  /* indent wrapped lines */
    75  }
    76  
    77  li.ssa-value-list {
    78      display: inline;
    79  }
    80  
    81  li.ssa-start-block {
    82      padding: 0;
    83      margin: 0;
    84  }
    85  
    86  li.ssa-end-block {
    87      padding: 0;
    88      margin: 0;
    89  }
    90  
    91  ul.ssa-print-func {
    92      padding-left: 0;
    93  }
    94  
    95  dl.ssa-gen {
    96      padding-left: 0;
    97  }
    98  
    99  dt.ssa-prog-src {
   100      padding: 0;
   101      margin: 0;
   102      float: left;
   103      width: 4em;
   104  }
   105  
   106  dd.ssa-prog {
   107      padding: 0;
   108      margin-right: 0;
   109      margin-left: 4em;
   110  }
   111  
   112  .dead-value {
   113      color: gray;
   114  }
   115  
   116  .dead-block {
   117      opacity: 0.5;
   118  }
   119  
   120  .depcycle {
   121      font-style: italic;
   122  }
   123  
   124  .highlight-yellow         { background-color: yellow; }
   125  .highlight-aquamarine     { background-color: aquamarine; }
   126  .highlight-coral          { background-color: coral; }
   127  .highlight-lightpink      { background-color: lightpink; }
   128  .highlight-lightsteelblue { background-color: lightsteelblue; }
   129  .highlight-palegreen      { background-color: palegreen; }
   130  .highlight-powderblue     { background-color: powderblue; }
   131  .highlight-lightgray      { background-color: lightgray; }
   132  
   133  .outline-blue           { outline: blue solid 2px; }
   134  .outline-red            { outline: red solid 2px; }
   135  .outline-blueviolet     { outline: blueviolet solid 2px; }
   136  .outline-darkolivegreen { outline: darkolivegreen solid 2px; }
   137  .outline-fuchsia        { outline: fuchsia solid 2px; }
   138  .outline-sienna         { outline: sienna solid 2px; }
   139  .outline-gold           { outline: gold solid 2px; }
   140  
   141  </style>
   142  
   143  <script type="text/javascript">
   144  // ordered list of all available highlight colors
   145  var highlights = [
   146      "highlight-aquamarine",
   147      "highlight-coral",
   148      "highlight-lightpink",
   149      "highlight-lightsteelblue",
   150      "highlight-palegreen",
   151      "highlight-lightgray",
   152      "highlight-yellow"
   153  ];
   154  
   155  // state: which value is highlighted this color?
   156  var highlighted = {};
   157  for (var i = 0; i < highlights.length; i++) {
   158      highlighted[highlights[i]] = "";
   159  }
   160  
   161  // ordered list of all available outline colors
   162  var outlines = [
   163      "outline-blue",
   164      "outline-red",
   165      "outline-blueviolet",
   166      "outline-darkolivegreen",
   167      "outline-fuchsia",
   168      "outline-sienna",
   169      "outline-gold"
   170  ];
   171  
   172  // state: which value is outlined this color?
   173  var outlined = {};
   174  for (var i = 0; i < outlines.length; i++) {
   175      outlined[outlines[i]] = "";
   176  }
   177  
   178  window.onload = function() {
   179      var ssaElemClicked = function(elem, event, selections, selected) {
   180          event.stopPropagation()
   181  
   182          // TODO: pushState with updated state and read it on page load,
   183          // so that state can survive across reloads
   184  
   185          // find all values with the same name
   186          var c = elem.classList.item(0);
   187          var x = document.getElementsByClassName(c);
   188  
   189          // if selected, remove selections from all of them
   190          // otherwise, attempt to add
   191  
   192          var remove = "";
   193          for (var i = 0; i < selections.length; i++) {
   194              var color = selections[i];
   195              if (selected[color] == c) {
   196                  remove = color;
   197                  break;
   198              }
   199          }
   200  
   201          if (remove != "") {
   202              for (var i = 0; i < x.length; i++) {
   203                  x[i].classList.remove(remove);
   204              }
   205              selected[remove] = "";
   206              return;
   207          }
   208  
   209          // we're adding a selection
   210          // find first available color
   211          var avail = "";
   212          for (var i = 0; i < selections.length; i++) {
   213              var color = selections[i];
   214              if (selected[color] == "") {
   215                  avail = color;
   216                  break;
   217              }
   218          }
   219          if (avail == "") {
   220              alert("out of selection colors; go add more");
   221              return;
   222          }
   223  
   224          // set that as the selection
   225          for (var i = 0; i < x.length; i++) {
   226              x[i].classList.add(avail);
   227          }
   228          selected[avail] = c;
   229      };
   230  
   231      var ssaValueClicked = function(event) {
   232          ssaElemClicked(this, event, highlights, highlighted);
   233      }
   234  
   235      var ssaBlockClicked = function(event) {
   236          ssaElemClicked(this, event, outlines, outlined);
   237      }
   238  
   239      var ssavalues = document.getElementsByClassName("ssa-value");
   240      for (var i = 0; i < ssavalues.length; i++) {
   241          ssavalues[i].addEventListener('click', ssaValueClicked);
   242      }
   243  
   244      var ssalongvalues = document.getElementsByClassName("ssa-long-value");
   245      for (var i = 0; i < ssalongvalues.length; i++) {
   246          // don't attach listeners to li nodes, just the spans they contain
   247          if (ssalongvalues[i].nodeName == "SPAN") {
   248              ssalongvalues[i].addEventListener('click', ssaValueClicked);
   249          }
   250      }
   251  
   252      var ssablocks = document.getElementsByClassName("ssa-block");
   253      for (var i = 0; i < ssablocks.length; i++) {
   254          ssablocks[i].addEventListener('click', ssaBlockClicked);
   255      }
   256  };
   257  
   258  function toggle_visibility(id) {
   259     var e = document.getElementById(id);
   260     if(e.style.display == 'block')
   261        e.style.display = 'none';
   262     else
   263        e.style.display = 'block';
   264  }
   265  </script>
   266  
   267  </head>`)
   268  	w.WriteString("<body>")
   269  	w.WriteString("<h1>")
   270  	w.WriteString(html.EscapeString(name))
   271  	w.WriteString("</h1>")
   272  	w.WriteString(`
   273  <a href="#" onclick="toggle_visibility('help');" id="helplink">help</a>
   274  <div id="help">
   275  
   276  <p>
   277  Click on a value or block to toggle highlighting of that value/block
   278  and its uses.  (Values and blocks are highlighted by ID, and IDs of
   279  dead items may be reused, so not all highlights necessarily correspond
   280  to the clicked item.)
   281  </p>
   282  
   283  <p>
   284  Faded out values and blocks are dead code that has not been eliminated.
   285  </p>
   286  
   287  <p>
   288  Values printed in italics have a dependency cycle.
   289  </p>
   290  
   291  </div>
   292  `)
   293  	w.WriteString("<table>")
   294  	w.WriteString("<tr>")
   295  }
   296  
   297  func (w *HTMLWriter) Close() {
   298  	if w == nil {
   299  		return
   300  	}
   301  	io.WriteString(w.w, "</tr>")
   302  	io.WriteString(w.w, "</table>")
   303  	io.WriteString(w.w, "</body>")
   304  	io.WriteString(w.w, "</html>")
   305  	w.w.Close()
   306  }
   307  
   308  // WriteFunc writes f in a column headed by title.
   309  func (w *HTMLWriter) WriteFunc(title string, f *Func) {
   310  	if w == nil {
   311  		return // avoid generating HTML just to discard it
   312  	}
   313  	w.WriteColumn(title, f.HTML())
   314  	// TODO: Add visual representation of f's CFG.
   315  }
   316  
   317  // WriteColumn writes raw HTML in a column headed by title.
   318  // It is intended for pre- and post-compilation log output.
   319  func (w *HTMLWriter) WriteColumn(title string, html string) {
   320  	if w == nil {
   321  		return
   322  	}
   323  	w.WriteString("<td>")
   324  	w.WriteString("<h2>" + title + "</h2>")
   325  	w.WriteString(html)
   326  	w.WriteString("</td>")
   327  }
   328  
   329  func (w *HTMLWriter) Printf(msg string, v ...interface{}) {
   330  	if _, err := fmt.Fprintf(w.w, msg, v...); err != nil {
   331  		w.Fatalf(src.NoXPos, "%v", err)
   332  	}
   333  }
   334  
   335  func (w *HTMLWriter) WriteString(s string) {
   336  	if _, err := io.WriteString(w.w, s); err != nil {
   337  		w.Fatalf(src.NoXPos, "%v", err)
   338  	}
   339  }
   340  
   341  func (v *Value) HTML() string {
   342  	// TODO: Using the value ID as the class ignores the fact
   343  	// that value IDs get recycled and that some values
   344  	// are transmuted into other values.
   345  	s := v.String()
   346  	return fmt.Sprintf("<span class=\"%s ssa-value\">%s</span>", s, s)
   347  }
   348  
   349  func (v *Value) LongHTML() string {
   350  	// TODO: Any intra-value formatting?
   351  	// I'm wary of adding too much visual noise,
   352  	// but a little bit might be valuable.
   353  	// We already have visual noise in the form of punctuation
   354  	// maybe we could replace some of that with formatting.
   355  	s := fmt.Sprintf("<span class=\"%s ssa-long-value\">", v.String())
   356  	s += fmt.Sprintf("%s = %s", v.HTML(), v.Op.String())
   357  	s += " &lt;" + html.EscapeString(v.Type.String()) + "&gt;"
   358  	s += html.EscapeString(v.auxString())
   359  	for _, a := range v.Args {
   360  		s += fmt.Sprintf(" %s", a.HTML())
   361  	}
   362  	r := v.Block.Func.RegAlloc
   363  	if int(v.ID) < len(r) && r[v.ID] != nil {
   364  		s += " : " + html.EscapeString(r[v.ID].String())
   365  	}
   366  	var names []string
   367  	for name, values := range v.Block.Func.NamedValues {
   368  		for _, value := range values {
   369  			if value == v {
   370  				names = append(names, name.String())
   371  				break // drop duplicates.
   372  			}
   373  		}
   374  	}
   375  	if len(names) != 0 {
   376  		s += " (" + strings.Join(names, ", ") + ")"
   377  	}
   378  	s += "</span>"
   379  	return s
   380  }
   381  
   382  func (b *Block) HTML() string {
   383  	// TODO: Using the value ID as the class ignores the fact
   384  	// that value IDs get recycled and that some values
   385  	// are transmuted into other values.
   386  	s := html.EscapeString(b.String())
   387  	return fmt.Sprintf("<span class=\"%s ssa-block\">%s</span>", s, s)
   388  }
   389  
   390  func (b *Block) LongHTML() string {
   391  	// TODO: improve this for HTML?
   392  	s := fmt.Sprintf("<span class=\"%s ssa-block\">%s</span>", html.EscapeString(b.String()), html.EscapeString(b.Kind.String()))
   393  	if b.Aux != nil {
   394  		s += html.EscapeString(fmt.Sprintf(" {%v}", b.Aux))
   395  	}
   396  	if b.Control != nil {
   397  		s += fmt.Sprintf(" %s", b.Control.HTML())
   398  	}
   399  	if len(b.Succs) > 0 {
   400  		s += " &#8594;" // right arrow
   401  		for _, e := range b.Succs {
   402  			c := e.b
   403  			s += " " + c.HTML()
   404  		}
   405  	}
   406  	switch b.Likely {
   407  	case BranchUnlikely:
   408  		s += " (unlikely)"
   409  	case BranchLikely:
   410  		s += " (likely)"
   411  	}
   412  	return s
   413  }
   414  
   415  func (f *Func) HTML() string {
   416  	var buf bytes.Buffer
   417  	fmt.Fprint(&buf, "<code>")
   418  	p := htmlFuncPrinter{w: &buf}
   419  	fprintFunc(p, f)
   420  
   421  	// fprintFunc(&buf, f) // TODO: HTML, not text, <br /> for line breaks, etc.
   422  	fmt.Fprint(&buf, "</code>")
   423  	return buf.String()
   424  }
   425  
   426  type htmlFuncPrinter struct {
   427  	w io.Writer
   428  }
   429  
   430  func (p htmlFuncPrinter) header(f *Func) {}
   431  
   432  func (p htmlFuncPrinter) startBlock(b *Block, reachable bool) {
   433  	// TODO: Make blocks collapsable?
   434  	var dead string
   435  	if !reachable {
   436  		dead = "dead-block"
   437  	}
   438  	fmt.Fprintf(p.w, "<ul class=\"%s ssa-print-func %s\">", b, dead)
   439  	fmt.Fprintf(p.w, "<li class=\"ssa-start-block\">%s:", b.HTML())
   440  	if len(b.Preds) > 0 {
   441  		io.WriteString(p.w, " &#8592;") // left arrow
   442  		for _, e := range b.Preds {
   443  			pred := e.b
   444  			fmt.Fprintf(p.w, " %s", pred.HTML())
   445  		}
   446  	}
   447  	io.WriteString(p.w, "</li>")
   448  	if len(b.Values) > 0 { // start list of values
   449  		io.WriteString(p.w, "<li class=\"ssa-value-list\">")
   450  		io.WriteString(p.w, "<ul>")
   451  	}
   452  }
   453  
   454  func (p htmlFuncPrinter) endBlock(b *Block) {
   455  	if len(b.Values) > 0 { // end list of values
   456  		io.WriteString(p.w, "</ul>")
   457  		io.WriteString(p.w, "</li>")
   458  	}
   459  	io.WriteString(p.w, "<li class=\"ssa-end-block\">")
   460  	fmt.Fprint(p.w, b.LongHTML())
   461  	io.WriteString(p.w, "</li>")
   462  	io.WriteString(p.w, "</ul>")
   463  	// io.WriteString(p.w, "</span>")
   464  }
   465  
   466  func (p htmlFuncPrinter) value(v *Value, live bool) {
   467  	var dead string
   468  	if !live {
   469  		dead = "dead-value"
   470  	}
   471  	fmt.Fprintf(p.w, "<li class=\"ssa-long-value %s\">", dead)
   472  	fmt.Fprint(p.w, v.LongHTML())
   473  	io.WriteString(p.w, "</li>")
   474  }
   475  
   476  func (p htmlFuncPrinter) startDepCycle() {
   477  	fmt.Fprintln(p.w, "<span class=\"depcycle\">")
   478  }
   479  
   480  func (p htmlFuncPrinter) endDepCycle() {
   481  	fmt.Fprintln(p.w, "</span>")
   482  }
   483  
   484  func (p htmlFuncPrinter) named(n LocalSlot, vals []*Value) {
   485  	fmt.Fprintf(p.w, "<li>name %s: ", n)
   486  	for _, val := range vals {
   487  		fmt.Fprintf(p.w, "%s ", val.HTML())
   488  	}
   489  	fmt.Fprintf(p.w, "</li>")
   490  }