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 += " <" + html.EscapeString(v.Type.String()) + ">" 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 += " →" // 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, " ←") // 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 }