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