github.com/mattn/go@v0.0.0-20171011075504-07f7db3ea99f/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 li { 70 list-style-type: none; 71 } 72 73 li.ssa-long-value { 74 text-indent: -2em; /* indent wrapped lines */ 75 } 76 77 li.ssa-value-list { 78 display: inline; 79 } 80 81 li.ssa-start-block { 82 padding: 0; 83 margin: 0; 84 } 85 86 li.ssa-end-block { 87 padding: 0; 88 margin: 0; 89 } 90 91 ul.ssa-print-func { 92 padding-left: 0; 93 } 94 95 dl.ssa-gen { 96 padding-left: 0; 97 } 98 99 dt.ssa-prog-src { 100 padding: 0; 101 margin: 0; 102 float: left; 103 width: 4em; 104 } 105 106 dd.ssa-prog { 107 padding: 0; 108 margin-right: 0; 109 margin-left: 4em; 110 } 111 112 .dead-value { 113 color: gray; 114 } 115 116 .dead-block { 117 opacity: 0.5; 118 } 119 120 .depcycle { 121 font-style: italic; 122 } 123 124 .highlight-yellow { background-color: yellow; } 125 .highlight-aquamarine { background-color: aquamarine; } 126 .highlight-coral { background-color: coral; } 127 .highlight-lightpink { background-color: lightpink; } 128 .highlight-lightsteelblue { background-color: lightsteelblue; } 129 .highlight-palegreen { background-color: palegreen; } 130 .highlight-powderblue { background-color: powderblue; } 131 .highlight-lightgray { background-color: lightgray; } 132 133 .outline-blue { outline: blue solid 2px; } 134 .outline-red { outline: red solid 2px; } 135 .outline-blueviolet { outline: blueviolet solid 2px; } 136 .outline-darkolivegreen { outline: darkolivegreen solid 2px; } 137 .outline-fuchsia { outline: fuchsia solid 2px; } 138 .outline-sienna { outline: sienna solid 2px; } 139 .outline-gold { outline: gold solid 2px; } 140 141 </style> 142 143 <script type="text/javascript"> 144 // ordered list of all available highlight colors 145 var highlights = [ 146 "highlight-aquamarine", 147 "highlight-coral", 148 "highlight-lightpink", 149 "highlight-lightsteelblue", 150 "highlight-palegreen", 151 "highlight-lightgray", 152 "highlight-yellow" 153 ]; 154 155 // state: which value is highlighted this color? 156 var highlighted = {}; 157 for (var i = 0; i < highlights.length; i++) { 158 highlighted[highlights[i]] = ""; 159 } 160 161 // ordered list of all available outline colors 162 var outlines = [ 163 "outline-blue", 164 "outline-red", 165 "outline-blueviolet", 166 "outline-darkolivegreen", 167 "outline-fuchsia", 168 "outline-sienna", 169 "outline-gold" 170 ]; 171 172 // state: which value is outlined this color? 173 var outlined = {}; 174 for (var i = 0; i < outlines.length; i++) { 175 outlined[outlines[i]] = ""; 176 } 177 178 window.onload = function() { 179 var ssaElemClicked = function(elem, event, selections, selected) { 180 event.stopPropagation() 181 182 // TODO: pushState with updated state and read it on page load, 183 // so that state can survive across reloads 184 185 // find all values with the same name 186 var c = elem.classList.item(0); 187 var x = document.getElementsByClassName(c); 188 189 // if selected, remove selections from all of them 190 // otherwise, attempt to add 191 192 var remove = ""; 193 for (var i = 0; i < selections.length; i++) { 194 var color = selections[i]; 195 if (selected[color] == c) { 196 remove = color; 197 break; 198 } 199 } 200 201 if (remove != "") { 202 for (var i = 0; i < x.length; i++) { 203 x[i].classList.remove(remove); 204 } 205 selected[remove] = ""; 206 return; 207 } 208 209 // we're adding a selection 210 // find first available color 211 var avail = ""; 212 for (var i = 0; i < selections.length; i++) { 213 var color = selections[i]; 214 if (selected[color] == "") { 215 avail = color; 216 break; 217 } 218 } 219 if (avail == "") { 220 alert("out of selection colors; go add more"); 221 return; 222 } 223 224 // set that as the selection 225 for (var i = 0; i < x.length; i++) { 226 x[i].classList.add(avail); 227 } 228 selected[avail] = c; 229 }; 230 231 var ssaValueClicked = function(event) { 232 ssaElemClicked(this, event, highlights, highlighted); 233 } 234 235 var ssaBlockClicked = function(event) { 236 ssaElemClicked(this, event, outlines, outlined); 237 } 238 239 var ssavalues = document.getElementsByClassName("ssa-value"); 240 for (var i = 0; i < ssavalues.length; i++) { 241 ssavalues[i].addEventListener('click', ssaValueClicked); 242 } 243 244 var ssalongvalues = document.getElementsByClassName("ssa-long-value"); 245 for (var i = 0; i < ssalongvalues.length; i++) { 246 // don't attach listeners to li nodes, just the spans they contain 247 if (ssalongvalues[i].nodeName == "SPAN") { 248 ssalongvalues[i].addEventListener('click', ssaValueClicked); 249 } 250 } 251 252 var ssablocks = document.getElementsByClassName("ssa-block"); 253 for (var i = 0; i < ssablocks.length; i++) { 254 ssablocks[i].addEventListener('click', ssaBlockClicked); 255 } 256 }; 257 258 function toggle_visibility(id) { 259 var e = document.getElementById(id); 260 if(e.style.display == 'block') 261 e.style.display = 'none'; 262 else 263 e.style.display = 'block'; 264 } 265 </script> 266 267 </head>`) 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 io.WriteString(w.w, "</tr>") 302 io.WriteString(w.w, "</table>") 303 io.WriteString(w.w, "</body>") 304 io.WriteString(w.w, "</html>") 305 w.w.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.w, msg, v...); err != nil { 331 w.Fatalf(src.NoXPos, "%v", err) 332 } 333 } 334 335 func (w *HTMLWriter) WriteString(s string) { 336 if _, err := io.WriteString(w.w, s); err != nil { 337 w.Fatalf(src.NoXPos, "%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].String()) 365 } 366 var names []string 367 for name, values := range v.Block.Func.NamedValues { 368 for _, value := range values { 369 if value == v { 370 names = append(names, name.String()) 371 break // drop duplicates. 372 } 373 } 374 } 375 if len(names) != 0 { 376 s += " (" + strings.Join(names, ", ") + ")" 377 } 378 s += "</span>" 379 return s 380 } 381 382 func (b *Block) HTML() string { 383 // TODO: Using the value ID as the class ignores the fact 384 // that value IDs get recycled and that some values 385 // are transmuted into other values. 386 s := html.EscapeString(b.String()) 387 return fmt.Sprintf("<span class=\"%s ssa-block\">%s</span>", s, s) 388 } 389 390 func (b *Block) LongHTML() string { 391 // TODO: improve this for HTML? 392 s := fmt.Sprintf("<span class=\"%s ssa-block\">%s</span>", html.EscapeString(b.String()), html.EscapeString(b.Kind.String())) 393 if b.Aux != nil { 394 s += html.EscapeString(fmt.Sprintf(" {%v}", b.Aux)) 395 } 396 if b.Control != nil { 397 s += fmt.Sprintf(" %s", b.Control.HTML()) 398 } 399 if len(b.Succs) > 0 { 400 s += " →" // right arrow 401 for _, e := range b.Succs { 402 c := e.b 403 s += " " + c.HTML() 404 } 405 } 406 switch b.Likely { 407 case BranchUnlikely: 408 s += " (unlikely)" 409 case BranchLikely: 410 s += " (likely)" 411 } 412 return s 413 } 414 415 func (f *Func) HTML() string { 416 var buf bytes.Buffer 417 fmt.Fprint(&buf, "<code>") 418 p := htmlFuncPrinter{w: &buf} 419 fprintFunc(p, f) 420 421 // fprintFunc(&buf, f) // TODO: HTML, not text, <br /> for line breaks, etc. 422 fmt.Fprint(&buf, "</code>") 423 return buf.String() 424 } 425 426 type htmlFuncPrinter struct { 427 w io.Writer 428 } 429 430 func (p htmlFuncPrinter) header(f *Func) {} 431 432 func (p htmlFuncPrinter) startBlock(b *Block, reachable bool) { 433 // TODO: Make blocks collapsable? 434 var dead string 435 if !reachable { 436 dead = "dead-block" 437 } 438 fmt.Fprintf(p.w, "<ul class=\"%s ssa-print-func %s\">", b, dead) 439 fmt.Fprintf(p.w, "<li class=\"ssa-start-block\">%s:", b.HTML()) 440 if len(b.Preds) > 0 { 441 io.WriteString(p.w, " ←") // left arrow 442 for _, e := range b.Preds { 443 pred := e.b 444 fmt.Fprintf(p.w, " %s", pred.HTML()) 445 } 446 } 447 io.WriteString(p.w, "</li>") 448 if len(b.Values) > 0 { // start list of values 449 io.WriteString(p.w, "<li class=\"ssa-value-list\">") 450 io.WriteString(p.w, "<ul>") 451 } 452 } 453 454 func (p htmlFuncPrinter) endBlock(b *Block) { 455 if len(b.Values) > 0 { // end list of values 456 io.WriteString(p.w, "</ul>") 457 io.WriteString(p.w, "</li>") 458 } 459 io.WriteString(p.w, "<li class=\"ssa-end-block\">") 460 fmt.Fprint(p.w, b.LongHTML()) 461 io.WriteString(p.w, "</li>") 462 io.WriteString(p.w, "</ul>") 463 // io.WriteString(p.w, "</span>") 464 } 465 466 func (p htmlFuncPrinter) value(v *Value, live bool) { 467 var dead string 468 if !live { 469 dead = "dead-value" 470 } 471 fmt.Fprintf(p.w, "<li class=\"ssa-long-value %s\">", dead) 472 fmt.Fprint(p.w, v.LongHTML()) 473 io.WriteString(p.w, "</li>") 474 } 475 476 func (p htmlFuncPrinter) startDepCycle() { 477 fmt.Fprintln(p.w, "<span class=\"depcycle\">") 478 } 479 480 func (p htmlFuncPrinter) endDepCycle() { 481 fmt.Fprintln(p.w, "</span>") 482 } 483 484 func (p htmlFuncPrinter) named(n LocalSlot, vals []*Value) { 485 fmt.Fprintf(p.w, "<li>name %s: ", n) 486 for _, val := range vals { 487 fmt.Fprintf(p.w, "%s ", val.HTML()) 488 } 489 fmt.Fprintf(p.w, "</li>") 490 }