github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/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
   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  	w.WriteString("</tr>")
   302  	w.WriteString("</table>")
   303  	w.WriteString("</body>")
   304  	w.WriteString("</html>")
   305  	w.File.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.File, msg, v...); err != nil {
   331  		w.Fatalf(0, "%v", err)
   332  	}
   333  }
   334  
   335  func (w *HTMLWriter) WriteString(s string) {
   336  	if _, err := w.File.WriteString(s); err != nil {
   337  		w.Fatalf(0, "%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].Name())
   365  	}
   366  	s += "</span>"
   367  	return s
   368  }
   369  
   370  func (b *Block) HTML() string {
   371  	// TODO: Using the value ID as the class ignores the fact
   372  	// that value IDs get recycled and that some values
   373  	// are transmuted into other values.
   374  	s := html.EscapeString(b.String())
   375  	return fmt.Sprintf("<span class=\"%s ssa-block\">%s</span>", s, s)
   376  }
   377  
   378  func (b *Block) LongHTML() string {
   379  	// TODO: improve this for HTML?
   380  	s := fmt.Sprintf("<span class=\"%s ssa-block\">%s</span>", html.EscapeString(b.String()), html.EscapeString(b.Kind.String()))
   381  	if b.Aux != nil {
   382  		s += html.EscapeString(fmt.Sprintf(" {%v}", b.Aux))
   383  	}
   384  	if b.Control != nil {
   385  		s += fmt.Sprintf(" %s", b.Control.HTML())
   386  	}
   387  	if len(b.Succs) > 0 {
   388  		s += " &#8594;" // right arrow
   389  		for _, e := range b.Succs {
   390  			c := e.b
   391  			s += " " + c.HTML()
   392  		}
   393  	}
   394  	switch b.Likely {
   395  	case BranchUnlikely:
   396  		s += " (unlikely)"
   397  	case BranchLikely:
   398  		s += " (likely)"
   399  	}
   400  	return s
   401  }
   402  
   403  func (f *Func) HTML() string {
   404  	var buf bytes.Buffer
   405  	fmt.Fprint(&buf, "<code>")
   406  	p := htmlFuncPrinter{w: &buf}
   407  	fprintFunc(p, f)
   408  
   409  	// fprintFunc(&buf, f) // TODO: HTML, not text, <br /> for line breaks, etc.
   410  	fmt.Fprint(&buf, "</code>")
   411  	return buf.String()
   412  }
   413  
   414  type htmlFuncPrinter struct {
   415  	w io.Writer
   416  }
   417  
   418  func (p htmlFuncPrinter) header(f *Func) {}
   419  
   420  func (p htmlFuncPrinter) startBlock(b *Block, reachable bool) {
   421  	// TODO: Make blocks collapsable?
   422  	var dead string
   423  	if !reachable {
   424  		dead = "dead-block"
   425  	}
   426  	fmt.Fprintf(p.w, "<ul class=\"%s ssa-print-func %s\">", b, dead)
   427  	fmt.Fprintf(p.w, "<li class=\"ssa-start-block\">%s:", b.HTML())
   428  	if len(b.Preds) > 0 {
   429  		io.WriteString(p.w, " &#8592;") // left arrow
   430  		for _, e := range b.Preds {
   431  			pred := e.b
   432  			fmt.Fprintf(p.w, " %s", pred.HTML())
   433  		}
   434  	}
   435  	io.WriteString(p.w, "</li>")
   436  	if len(b.Values) > 0 { // start list of values
   437  		io.WriteString(p.w, "<li class=\"ssa-value-list\">")
   438  		io.WriteString(p.w, "<ul>")
   439  	}
   440  }
   441  
   442  func (p htmlFuncPrinter) endBlock(b *Block) {
   443  	if len(b.Values) > 0 { // end list of values
   444  		io.WriteString(p.w, "</ul>")
   445  		io.WriteString(p.w, "</li>")
   446  	}
   447  	io.WriteString(p.w, "<li class=\"ssa-end-block\">")
   448  	fmt.Fprint(p.w, b.LongHTML())
   449  	io.WriteString(p.w, "</li>")
   450  	io.WriteString(p.w, "</ul>")
   451  	// io.WriteString(p.w, "</span>")
   452  }
   453  
   454  func (p htmlFuncPrinter) value(v *Value, live bool) {
   455  	var dead string
   456  	if !live {
   457  		dead = "dead-value"
   458  	}
   459  	fmt.Fprintf(p.w, "<li class=\"ssa-long-value %s\">", dead)
   460  	fmt.Fprint(p.w, v.LongHTML())
   461  	io.WriteString(p.w, "</li>")
   462  }
   463  
   464  func (p htmlFuncPrinter) startDepCycle() {
   465  	fmt.Fprintln(p.w, "<span class=\"depcycle\">")
   466  }
   467  
   468  func (p htmlFuncPrinter) endDepCycle() {
   469  	fmt.Fprintln(p.w, "</span>")
   470  }
   471  
   472  func (p htmlFuncPrinter) named(n LocalSlot, vals []*Value) {
   473  	// TODO
   474  }