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