github.com/sanprasirt/go@v0.0.0-20170607001320-a027466e4b6d/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  	w io.WriteCloser
    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{w: 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-aquamarine",
   146      "highlight-coral",
   147      "highlight-lightpink",
   148      "highlight-lightsteelblue",
   149      "highlight-palegreen",
   150      "highlight-lightgray",
   151      "highlight-yellow"
   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  	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
   277  and its uses.  (Values and blocks are highlighted by ID, and IDs of
   278  dead items may be reused, so not all highlights necessarily correspond
   279  to the clicked item.)
   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  	io.WriteString(w.w, "</tr>")
   301  	io.WriteString(w.w, "</table>")
   302  	io.WriteString(w.w, "</body>")
   303  	io.WriteString(w.w, "</html>")
   304  	w.w.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.w, msg, v...); err != nil {
   330  		w.Fatalf(src.NoXPos, "%v", err)
   331  	}
   332  }
   333  
   334  func (w *HTMLWriter) WriteString(s string) {
   335  	if _, err := io.WriteString(w.w, s); err != nil {
   336  		w.Fatalf(src.NoXPos, "%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  	s := v.String()
   345  	return fmt.Sprintf("<span class=\"%s ssa-value\">%s</span>", s, s)
   346  }
   347  
   348  func (v *Value) LongHTML() string {
   349  	// TODO: Any intra-value formatting?
   350  	// I'm wary of adding too much visual noise,
   351  	// but a little bit might be valuable.
   352  	// We already have visual noise in the form of punctuation
   353  	// maybe we could replace some of that with formatting.
   354  	s := fmt.Sprintf("<span class=\"%s ssa-long-value\">", v.String())
   355  	s += fmt.Sprintf("%s = %s", v.HTML(), v.Op.String())
   356  	s += " &lt;" + html.EscapeString(v.Type.String()) + "&gt;"
   357  	s += html.EscapeString(v.auxString())
   358  	for _, a := range v.Args {
   359  		s += fmt.Sprintf(" %s", a.HTML())
   360  	}
   361  	r := v.Block.Func.RegAlloc
   362  	if int(v.ID) < len(r) && r[v.ID] != nil {
   363  		s += " : " + html.EscapeString(r[v.ID].Name())
   364  	}
   365  	s += "</span>"
   366  	return s
   367  }
   368  
   369  func (b *Block) HTML() string {
   370  	// TODO: Using the value ID as the class ignores the fact
   371  	// that value IDs get recycled and that some values
   372  	// are transmuted into other values.
   373  	s := html.EscapeString(b.String())
   374  	return fmt.Sprintf("<span class=\"%s ssa-block\">%s</span>", s, s)
   375  }
   376  
   377  func (b *Block) LongHTML() string {
   378  	// TODO: improve this for HTML?
   379  	s := fmt.Sprintf("<span class=\"%s ssa-block\">%s</span>", html.EscapeString(b.String()), html.EscapeString(b.Kind.String()))
   380  	if b.Aux != nil {
   381  		s += html.EscapeString(fmt.Sprintf(" {%v}", b.Aux))
   382  	}
   383  	if b.Control != nil {
   384  		s += fmt.Sprintf(" %s", b.Control.HTML())
   385  	}
   386  	if len(b.Succs) > 0 {
   387  		s += " &#8594;" // right arrow
   388  		for _, e := range b.Succs {
   389  			c := e.b
   390  			s += " " + c.HTML()
   391  		}
   392  	}
   393  	switch b.Likely {
   394  	case BranchUnlikely:
   395  		s += " (unlikely)"
   396  	case BranchLikely:
   397  		s += " (likely)"
   398  	}
   399  	return s
   400  }
   401  
   402  func (f *Func) HTML() string {
   403  	var buf bytes.Buffer
   404  	fmt.Fprint(&buf, "<code>")
   405  	p := htmlFuncPrinter{w: &buf}
   406  	fprintFunc(p, f)
   407  
   408  	// fprintFunc(&buf, f) // TODO: HTML, not text, <br /> for line breaks, etc.
   409  	fmt.Fprint(&buf, "</code>")
   410  	return buf.String()
   411  }
   412  
   413  type htmlFuncPrinter struct {
   414  	w io.Writer
   415  }
   416  
   417  func (p htmlFuncPrinter) header(f *Func) {}
   418  
   419  func (p htmlFuncPrinter) startBlock(b *Block, reachable bool) {
   420  	// TODO: Make blocks collapsable?
   421  	var dead string
   422  	if !reachable {
   423  		dead = "dead-block"
   424  	}
   425  	fmt.Fprintf(p.w, "<ul class=\"%s ssa-print-func %s\">", b, dead)
   426  	fmt.Fprintf(p.w, "<li class=\"ssa-start-block\">%s:", b.HTML())
   427  	if len(b.Preds) > 0 {
   428  		io.WriteString(p.w, " &#8592;") // left arrow
   429  		for _, e := range b.Preds {
   430  			pred := e.b
   431  			fmt.Fprintf(p.w, " %s", pred.HTML())
   432  		}
   433  	}
   434  	io.WriteString(p.w, "</li>")
   435  	if len(b.Values) > 0 { // start list of values
   436  		io.WriteString(p.w, "<li class=\"ssa-value-list\">")
   437  		io.WriteString(p.w, "<ul>")
   438  	}
   439  }
   440  
   441  func (p htmlFuncPrinter) endBlock(b *Block) {
   442  	if len(b.Values) > 0 { // end list of values
   443  		io.WriteString(p.w, "</ul>")
   444  		io.WriteString(p.w, "</li>")
   445  	}
   446  	io.WriteString(p.w, "<li class=\"ssa-end-block\">")
   447  	fmt.Fprint(p.w, b.LongHTML())
   448  	io.WriteString(p.w, "</li>")
   449  	io.WriteString(p.w, "</ul>")
   450  	// io.WriteString(p.w, "</span>")
   451  }
   452  
   453  func (p htmlFuncPrinter) value(v *Value, live bool) {
   454  	var dead string
   455  	if !live {
   456  		dead = "dead-value"
   457  	}
   458  	fmt.Fprintf(p.w, "<li class=\"ssa-long-value %s\">", dead)
   459  	fmt.Fprint(p.w, v.LongHTML())
   460  	io.WriteString(p.w, "</li>")
   461  }
   462  
   463  func (p htmlFuncPrinter) startDepCycle() {
   464  	fmt.Fprintln(p.w, "<span class=\"depcycle\">")
   465  }
   466  
   467  func (p htmlFuncPrinter) endDepCycle() {
   468  	fmt.Fprintln(p.w, "</span>")
   469  }
   470  
   471  func (p htmlFuncPrinter) named(n LocalSlot, vals []*Value) {
   472  	fmt.Fprintf(p.w, "<li>name %s: ", n.Name())
   473  	for _, val := range vals {
   474  		fmt.Fprintf(p.w, "%s ", val.HTML())
   475  	}
   476  	fmt.Fprintf(p.w, "</li>")
   477  }