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