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