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