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