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